Initialize environment

library(caim)
caim::clr()

Setup preferences

# most important maturities
maturities_a = c(.25, 2, 5, 10)
# interesting maturities, level b
maturities_b = c(.0833, 1, 30)
# all other maturities will be level c

# relative weightings for each maturity level
# these will form a density function that will be normalised to sum to 1
#   , so don't worry about that here
MAT_A_WT = 1
MAT_B_WT = .75
MAT_C_WT = 0.15

# set of maturities to use for lambda values
lambda_maturities <- seq(0.5, 5, 0.5)

# k for k-fold evaluation
NUM_K_FOLDS = 10

Load G7 yield info

g7_rates_info <- fread("../../data/ratesinfo.csv")[
  country %in% c("US", "CA", "DE", "FR", "IT", "UK", "JP") & class=="GOVT"
  , .(country, class, maturity, id=bb_ticker)
]

g7_rates_data <- g7_rates_info[
  fread("../../data/ratesdata.csv")
  , 
  , on=.(id), nomatch=NULL 
][
  , .(country
      , class
      , maturity
      , date=lubridate::as_date(date)
      , year=lubridate::year(lubridate::as_date(date))
      , month=lubridate::month(lubridate::as_date(date))
      , yield=PX_LAST
      )
][
  , .SD, key=.(country, class, maturity, date)
]


g7_curve_mats <- sort(unique(g7_rates_data$maturity))
g7_curve_columns <- paste0("yield_", g7_curve_mats)

g7_curves <- dcast(
  g7_rates_data, country + class + date + year + month ~ maturity, value.var="yield"  
)[
  , .SD
  , key=.(country, class, date)  
]
setnames(g7_curves, c(key(g7_curves), g7_curve_mats), c(key(g7_curves), g7_curve_columns))

g7_curves[]

Load G7 Money market data

g7_mm_info <- data.table(
  country=c("US", "CA", "DE", "FR", "IT", "UK", "JP", "DE", "FR", "IT")
  , curncy=c("USD", "CAD", "EUR", "EUR", "EUR", "GBP", "JPY", "DEM", "FRF", "ITL")
  , id=c("USD.FIX.1M", "CAD.FIX.1M", "EUR.FIX.1M", "EUR.FIX.1M", "EUR.FIX.1M"
         , "GBP.FIX.1M", "JPY.FIX.1M", "DEM.FIX.1M", "FRF.FIX.1M", "ITL.FIX.1M")
)
# g7_mm_info <- fread("../data/mminfo.csv")[
#   CURNCY %in% c("USD",  "EUR", "GBP", "JPY", "CAD")
#   , .(curncy=CURNCY, id=FIX_1M)
# ]
g7_mm_data <- g7_mm_info[
  fread("../../data/mmdata.csv")
  , 
  , on=.(id), nomatch=NULL
][
  , .(country
      , curncy
      , date = lubridate::as_date(date)
      , year=lubridate::year(lubridate::as_date(date))
      , month=lubridate::month(lubridate::as_date(date))
      , maturity = 0.8333
      , yield = PX_LAST
      )
][
  , .SD , key=.(country, curncy, date)
]

# stitch together pre- and post-EUR convergence data for DE, FR, IT
eur_start_date <- min(g7_mm_data[country=="DE" & curncy=="EUR"]$date)
# delete local observations where EUR data is available
g7_mm_data <- g7_mm_data[!(curncy %in% c("DEM", "FRF", "ITL") & date >= eur_start_date)]
# relabel pre-EUR local observations as EUR
# may need to rethink this if we want to implement pre- and post-EUR currency data
# - maybe label everything as DEM FRF ITL?
g7_mm_data[curncy %in% c("DEM", "FRF", "ITL"), curncy := "EUR"] 

g7_mm_data[]

Create monthly data

g7_curves_m <- g7_curves[
  date %in% caim::month_end_dates(date)
]

g7_mm_data_m <- g7_mm_data[
  date %in% caim::month_end_dates(date)
]

Add money market data to short end of government curves

This is, admittedly, a big kludge, but should help when there are no government yields below 2-year maturity, like early Canada, etc. Also, this may be a good way to average 3-month bills and 1-month deposits as a proxy for cash when doing Nelson-Siegel analyses.

Assumption: We’ll do LIBOR - 1/8 as a crude proxy for LIBID

g7_curves_m[g7_mm_data_m, yield_0.0833 := i.yield - 0.125, on=.(country, year, month)][]

Ensure there are at least 3 yield points

# ensure there are at least 3 yield points
g7_curves_m <- g7_curves_m[
  g7_curves_m[ ,.(valid=(sum(!is.na(.SD)) >= 3)), by=.(country, class, date)]$valid
][
  # find first date that works for all countries
  date >= max(g7_curves_m[,.(min_date=min(date)), by=.(country, class)]$min_date)
]

Calculate Nelson Siegel coefficients

curve_data <- g7_curves_m
curve_mats <- g7_curve_mats
curve_columns <- g7_curve_columns

curve_coefs <- list()
curve_ids <- unique(curve_data[,.(country, class)])
for (c in 1:nrow(curve_ids)) {
  t_curve_data <- curve_data[country == curve_ids[c, country] & class == curve_ids[c, class]]
  t_curve_dates <- sort(unique(t_curve_data$date))
  t_curve_mats <- curve_mats #sort(unique(yield_info[curve_id==c]$maturity))
  t_curve_mat_names <- curve_columns #as.character(t_curve_mats)
  
  t_curve_mat_wts <- rep(MAT_C_WT, length(t_curve_mats))
  names(t_curve_mat_wts) <- t_curve_mat_names
  t_curve_mat_wts[names(t_curve_mat_wts) %in% maturities_a] <- MAT_A_WT
  t_curve_mat_wts[names(t_curve_mat_wts) %in% maturities_b] <- MAT_B_WT
  t_curve_mat_wts <- t_curve_mat_wts / sum(t_curve_mat_wts)
  
  
  t_res <- list()
  for (i in 1:length(lambda_maturities)) {
    mat <- lambda_maturities[i]
    lambda <- caim::ns_mat2lambda(mat)
    print(paste("calculating for curve:", c, curve_ids[c, country], curve_ids[c, class], "maturity:", mat, "lambda:", lambda))
    t_res[[i]] <- list(
      mat=mat
      , lambda=lambda
      , coefs=data.table(
        country=curve_ids[c, country]
        , class=curve_ids[c, class]
        , date=t_curve_data[,date]
        , t(apply(t_curve_data, 1, function(x) 
          caim::ns_yields2coefs(t_curve_mats
                                , as.numeric(x[t_curve_mat_names])
                                , lambda=lambda
                                , wts=t_curve_mat_wts)))
        , lambda_mat=mat
        )
      )
  }
  
  # calculate summaries
  # 5 year halflife assuming 260 business days in a year
  decay <- halflife2decay(5*260)
  lambda_summary <- data.table(t(sapply(t_res, function(x) 
    c(mat=x$mat, lambda=x$lambda, wss=mean(x$coefs$wss), wss_exp=mean_exp(x$coefs$wss, decay)))))

  # choose best data
  best_hist_fit <- which(lambda_summary$wss == min(lambda_summary$wss))
  best_exp_fit <- which(lambda_summary$wss_exp == min(lambda_summary$wss_exp))
  # take average of best fits, but tilt towards best_exp_fit
  best_ix <- floor(mean(c(best_hist_fit, best_exp_fit))+ifelse(best_exp_fit > best_hist_fit, 0.5, 0))
  best_hist_coefs <- t_res[[best_ix]]$coefs
  # best_hist_coefs$curve_id <- c

  #add data to curve_coefs
  curve_coefs[[length(curve_coefs)+1]] <- best_hist_coefs
}
[1] "calculating for curve: 1 CA GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 1 CA GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 1 CA GOVT maturity: 1.5 lambda: 1.19552075559691"
[1] "calculating for curve: 1 CA GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 1 CA GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 1 CA GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 1 CA GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 1 CA GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 1 CA GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 1 CA GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 2 DE GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 2 DE GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 2 DE GOVT maturity: 1.5 lambda: 1.19552075559691"
[1] "calculating for curve: 2 DE GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 2 DE GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 2 DE GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 2 DE GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 2 DE GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 2 DE GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 2 DE GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 3 FR GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 3 FR GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 3 FR GOVT maturity: 1.5 lambda: 1.19552075559691"
[1] "calculating for curve: 3 FR GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 3 FR GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 3 FR GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 3 FR GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 3 FR GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 3 FR GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 3 FR GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 4 IT GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 4 IT GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 4 IT GOVT maturity: 1.5 lambda: 1.19552075559691"
[1] "calculating for curve: 4 IT GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 4 IT GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 4 IT GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 4 IT GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 4 IT GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 4 IT GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 4 IT GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 5 JP GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 5 JP GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 5 JP GOVT maturity: 1.5 lambda: 1.19552075559691"
[1] "calculating for curve: 5 JP GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 5 JP GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 5 JP GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 5 JP GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 5 JP GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 5 JP GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 5 JP GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 6 UK GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 6 UK GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 6 UK GOVT maturity: 1.5 lambda: 1.19552075559691"
[1] "calculating for curve: 6 UK GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 6 UK GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 6 UK GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 6 UK GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 6 UK GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 6 UK GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 6 UK GOVT maturity: 5 lambda: 0.358681854090179"
[1] "calculating for curve: 7 US GOVT maturity: 0.5 lambda: 3.58657038690834"
[1] "calculating for curve: 7 US GOVT maturity: 1 lambda: 1.79328063484169"
[1] "calculating for curve: 7 US GOVT maturity: 1.5 lambda: 1.19552075559691"
[1] "calculating for curve: 7 US GOVT maturity: 2 lambda: 0.896641550467829"
[1] "calculating for curve: 7 US GOVT maturity: 2.5 lambda: 0.717323931054624"
[1] "calculating for curve: 7 US GOVT maturity: 3 lambda: 0.59776033052309"
[1] "calculating for curve: 7 US GOVT maturity: 3.5 lambda: 0.512348203420652"
[1] "calculating for curve: 7 US GOVT maturity: 4 lambda: 0.448335176139679"
[1] "calculating for curve: 7 US GOVT maturity: 4.5 lambda: 0.398496343358628"
[1] "calculating for curve: 7 US GOVT maturity: 5 lambda: 0.358681854090179"
ns_coefs <- rbindlist(curve_coefs)[
  , .(ns_beta0 = beta0
      , ns_beta1 = beta1
      , ns_beta2 = beta2
      , ns_lambda = lambda
      , ns_lambda_mat = lambda_mat
      , ns_wss = wss
      )
  , key=.(country, class, date)
]

g7_curves_m <- g7_curves_m[ns_coefs]

rm(list=setdiff(ls(), c("g7_curves", "g7_curves_m", "g7_rates_data", "g7_rates_info", "g7_curve_columns", "g7_curve_mats")))

Yield calculations

long_data <- melt(
  g7_curves_m  
  , c("country", "class", "date", "ns_beta0", "ns_beta1", "ns_beta2", "ns_lambda")
  ,  patterns("yield_")
  , value.name="yield_now"
)[
  , maturity := as.numeric(stringr::str_remove(variable, "yield_"))
][
  , .(country, class, date, ns_beta0, ns_beta1, ns_beta2, ns_lambda, maturity, yield_now)
]

# fill in missing yields with interpolated data
# table with !is.na(yield)
yields_valid <- long_data[!is.na(yield_now)][, c("mat", "yld") := .(maturity, yield_now)]
# table mapping to <=
yields_lo <- yields_valid[long_data, .(country, date, maturity, m_lo0=mat, y_lo0=yld), on=.(country, date, maturity), roll=T]
# table mapping to >=
yields_hi <- yields_valid[long_data, .(country, date, maturity, m_hi0=mat, y_hi0=yld), on=.(country, date, maturity), roll=-Inf]

yields_lin <- yields_hi[
  yields_lo
  , .(
    country
    , date
    , maturity
    , yield_lin = ifelse(m_hi0 == m_lo0
                         , y_hi0
                         , y_lo0 + (maturity - m_lo0) * (y_hi0 - y_lo0) / (m_hi0 - m_lo0)
                         )
    , y_hi0
    , y_lo0
    , m_hi0
    , m_lo0
  )
  , on=.(country, date, maturity)
]

long_data <- long_data[yields_lin, on=.(country, date, maturity)]

long_data <- long_data[
  , c("num_coups", "mod_dur") := .(
    ifelse(maturity > 0.25, ifelse(country=="US", 2, 1), 0)
    , maturity
  )  
][
  maturity > 0.25
  , mod_dur := round(caim::modified_duration(yield_lin / 100, yield_lin / 100, maturity, num_coups), 6)
][
  , .(
    ns_beta0
    , ns_beta1
    , ns_beta2
    , ns_lambda
    , num_coups
    , mod_dur #= round(caim::modified_duration(yield_now / 100, yield_now / 100, maturity, 1), 6)
    , yield_now
    , yield_lin
    , y_hi0
    , y_lo0
    , m_hi0
    , m_lo0
    )
  , key=.(country, class, maturity, date)
][
  , yield_prev := shift(yield_lin), by = .(country, class, maturity)
][
  , c("y_hi1", "y_lo1", "m_hi1", "m_lo1") := .(
    yield_lin
    , shift(yield_lin)
    , maturity
    , shift(maturity)
  )
  , by = .(country, class, date)
][
  , yield_sell_lin := (
    y_lo1 + ((m_hi1 - 1/12) - m_lo1) * (y_hi1 - y_lo1) / (m_hi1 - m_lo1)
  )
][
  , yield_buy_ns := round(caim::ns_coefs2yields(
    mats = maturity
    , beta0 = shift(ns_beta0)
    , beta1 = shift(ns_beta1)
    , beta2 = shift(ns_beta2)
    , lambda = shift(ns_lambda)
  )$y, 4)
  , by = .(country, class, maturity)
][
  , yield_sell_ns := round(caim::ns_coefs2yields(
    mats = maturity - 1/2
    , beta0 = ns_beta0
    , beta1 = ns_beta1
    , beta2 = ns_beta2
    , lambda = ns_lambda
  )$y, 4)
][
  , coup_inc_lin := round(yield_prev / 1200, 6)
][
  , shift_inc := ifelse(maturity > 0.25, round((yield_lin - yield_prev) / 100 * -mod_dur, 6), 0)
][
  , tot_ret_shift := coup_inc_lin + shift_inc
][
  , dur_inc_lin := ifelse(
    maturity > 0.25
    , round(caim::bond_price(yield_sell_lin / 100, yield_prev / 100, maturity - 1/12, 1, 1) - 1, 6)
    , 0
    )
][
  , tot_ret_lin := coup_inc_lin + dur_inc_lin
][
  , coup_inc_ns := round(yield_buy_ns / 1200, 6)  
][
 , price_inc_ns := ifelse(
   maturity > 0.25
   , round(caim::bond_price(yield_sell_ns / 100, yield_buy_ns / 100, maturity - 1/12, 1, 1) - 1, 6)
   , 0
   )
][
  , tot_ret_ns := coup_inc_ns + price_inc_ns
]

long_data[country=="US"]

wide_data <- dcast(
  long_data
  ,country + class + date + ns_beta0 + ns_beta1 + ns_beta2 + ns_lambda ~ maturity
  , value.var = names(long_data)[!(names(long_data) %in% c("country", "class", "date", "ns_beta0", "ns_beta1", "ns_beta2", "ns_lambda", "maturity"))]

  # , value.var = c("mod_dur", "yield_now", "yield_prev", "yield_buy_ns", "yield_sell_ns"
  #                 , "coup_inc_lin", "dur_inc_lin", "coup_inc_ns", "price_inc_ns", "tot_ret_ns"
  #                 )
  )

wide_data[]

g7_return_data <- dcast(
  long_data[maturity %in% c(0.0833, 1, 2, 3, 5, 7, 10)]
  , country + class + date ~ maturity
  , value.var = c("coup_inc_lin", "tot_ret_shift", "tot_ret_lin", "tot_ret_ns")
)

g7_return_data[]

Get asset data

g7_assets <- data.table(
  id=c("US.GOVT.13", "US.GOVT.15", "US.GOVT.110"
       , "CA.GOVT.13", "CA.GOVT.15", "CA.GOVT.110"
       , "DE.GOVT.13", "DE.GOVT.15", "DE.GOVT.110"
       , "FR.GOVT.13", "FR.GOVT.15", "FR.GOVT.110"
       , "IT.GOVT.13", "IT.GOVT.15", "IT.GOVT.110"
       , "UK.GOVT.13", "UK.GOVT.15", "UK.GOVT.110"
       , "JP.GOVT.13", "JP.GOVT.15", "JP.GOVT.110"
       )
  , country=c(rep("US", 3), rep("CA", 3), rep("DE", 3), rep("FR", 3), rep("IT", 3)
              , rep("UK", 3), rep("JP", 3))
  , maturity=rep(c(13, 15, 110), 7)
  , key=c("country", "maturity")
)
g7_asset_info <- fread("../../data/assetinfo.csv")[suggname %in% g7_assets$id]

g7_asset_info[]

g7_asset_data <- g7_assets[
  fread("../../data/assetdata.csv")
  , .(
    country
    , maturity
    , id
    , date = lubridate::as_date(date)
    , year = lubridate::year(date)
    , month = lubridate::month(date)
    , index_nav=PX_LAST
    )
  , on=.(id), nomatch=NULL
][
  date %in% caim::month_end_dates(date)
  , .SD
  , key=.(country, maturity, date)
][
  , index_ret := round(index_nav / shift(index_nav) - 1, 6)
  , by=.(country, maturity)
]

g7_asset_data[]

Model Setup

g7_models <- list(
  list(name="x_g13_coup_bullet", type="coup_bullet", maturity=13
       , features=c("coup_inc_lin_2"), unity_coefs=T)
  , list(name="x_g13_shift_bullet", type="shift_bullet", maturity=13
         , features=c("tot_ret_shift_2"), unity_coefs=T)
  , list(name="x_g13_lin_bullet", type="lin_bullet", maturity=13
         , features=c("tot_ret_lin_2"), unity_coefs=T)
  , list(name="x_g13_ns_bullet", type="ns_bullet", maturity=13
         , features=c("tot_ret_ns_2"), unity_coefs=T)
  , list(name="x_g13_coup_n", type="coup_n", maturity=13
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3"), unity_coefs=T)
  , list(name="x_g13_shift_n", type="shift_n", maturity=13
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3"), unity_coefs=T)
  , list(name="x_g13_lin_n", type="lin_n", maturity=13
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3"), unity_coefs=T)
  , list(name="x_g13_ns_n", type="ns_n", maturity=13
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3"), unity_coefs=T)
  , list(name="x_g13_coup_ladder", type="coup_ladder", maturity=13
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3"), unity_coefs=F)
  , list(name="x_g13_shift_ladder", type="shift_ladder", maturity=13
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3"), unity_coefs=F)
  , list(name="x_g13_lin_ladder", type="lin_ladder", maturity=13
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3"), unity_coefs=F)
  , list(name="x_g13_ns_ladder", type="ns_ladder", maturity=13
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3"), unity_coefs=F)

  , list(name="x_g15_coup_bullet", type="coup_bullet", maturity=15
         , features=c("coup_inc_lin_3"), unity_coefs=T)
  , list(name="x_g15_shift_bullet", type="shift_bullet", maturity=15
         , features=c("tot_ret_shift_3"), unity_coefs=T)
  , list(name="x_g15_lin_bullet", type="lin_bullet", maturity=15
         , features=c("tot_ret_lin_3"), unity_coefs=T)
  , list(name="x_g15_ns_bullet", type="ns_bullet", maturity=15
         , features=c("tot_ret_ns_3"), unity_coefs=T)
  , list(name="x_g15_coup_n", type="coup_n", maturity=15
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5"), unity_coefs=T)
  , list(name="x_g15_shift_n", type="shift_n", maturity=15
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5"), unity_coefs=T)
  , list(name="x_g15_lin_n", type="lin_n", maturity=15
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5"), unity_coefs=T)
  , list(name="x_g15_ns_n", type="ns_n", maturity=15
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5"), unity_coefs=T)
  , list(name="x_g15_coup_ladder", type="coup_ladder", maturity=15
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5"), unity_coefs=F)
  , list(name="x_g15_shift_ladder", type="shift_ladder", maturity=15
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5"), unity_coefs=F)
  , list(name="x_g15_lin_ladder", type="lin_ladder", maturity=15
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5"), unity_coefs=F)
  , list(name="x_g15_ns_ladder", type="ns_ladder", maturity=15
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5"), unity_coefs=F)
  
  , list(name="x_g110_coup_bullet", type="coup_bullet", maturity=110
         , features=c("coup_inc_lin_5"), unity_coefs=T)
  , list(name="x_g110_shift_bullet", type="shift_bullet", maturity=110
         , features=c("tot_ret_shift_5"), unity_coefs=T)
  , list(name="x_g110_lin_bullet", type="lin_bullet", maturity=110
         , features=c("tot_ret_lin_5"), unity_coefs=T)
  , list(name="x_g110_ns_bullet", type="ns_bullet", maturity=110
         , features=c("tot_ret_ns_5"), unity_coefs=T)
  , list(name="x_g110_coup_n", type="coup_n", maturity=110
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5", "coup_inc_lin_7", "coup_inc_lin_10")
         , unity_coefs=T)
  , list(name="x_g110_shift_n", type="shift_n", maturity=110
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5", "tot_ret_shift_7", "tot_ret_shift_10")
         , unity_coefs=T)
  , list(name="x_g110_lin_n", type="lin_n", maturity=110
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5", "tot_ret_lin_7", "tot_ret_lin_10")
         , unity_coefs=T)
  , list(name="x_g110_ns_n", type="ns_n", maturity=110
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5", "tot_ret_ns_7", "tot_ret_ns_10")
         , unity_coefs=T)
  , list(name="x_g110_coup_ladder", type="coup_ladder", maturity=110
         , features=c("coup_inc_lin_1", "coup_inc_lin_2", "coup_inc_lin_3", "coup_inc_lin_5", "coup_inc_lin_7", "coup_inc_lin_10")
         , unity_coefs=F)
  , list(name="x_g110_shift_ladder", type="shift_ladder", maturity=110
         , features=c("tot_ret_shift_1", "tot_ret_shift_2", "tot_ret_shift_3", "tot_ret_shift_5", "tot_ret_shift_7", "tot_ret_shift_10")
         , unity_coefs=F)
  , list(name="x_g110_lin_ladder", type="lin_ladder", maturity=110
         , features=c("tot_ret_lin_1", "tot_ret_lin_2", "tot_ret_lin_3", "tot_ret_lin_5", "tot_ret_lin_7", "tot_ret_lin_10")
         , unity_coefs=F)
  , list(name="x_g110_ns_ladder", type="ns_ladder", maturity=110
         , features=c("tot_ret_ns_1", "tot_ret_ns_2", "tot_ret_ns_3", "tot_ret_ns_5", "tot_ret_ns_7", "tot_ret_ns_10")
         , unity_coefs=F)
)

g7_features <- sort(unique(rbindlist(lapply(g7_models, function(x) data.table(feature=x$features))))$feature)

Model evaluation - full history

K-fold cross validation

k_fold_eval <- rbindlist(apply(g7_assets, 1, function(asset) {
  y_data <- g7_asset_data[id==asset["id"]]
  x_data <- g7_return_data[country==asset["country"]][
    , c("year", "month") := .(
      lubridate::year(date)
      , lubridate::month(date)
    )
  ]
  # print(paste(asset["country"], asset["maturity"], nrow(y_data), nrow(x_data)))
  model_data <- y_data[
    x_data
    ,
    , on=.(year, month), nomatch=NULL
  ][
    , !c(
      "country", "i.country", "class", "maturity", "year", "month", "index_nav", "i.date"
    )
  ]

  has_invalid <- rowSums(is.na(model_data[ , ..g7_features])) > 0
  # print(paste(asset["id"], "rows:", nrow(model_data), "invalid:", sum(has_invalid)))

  model_data <- model_data[!has_invalid]
  # print(paste("clean rows:", nrow(model_data)))
  
  k_ix <- caim::k_fold_ix(nrow(model_data), NUM_K_FOLDS)
  
  asset_models <- lapply(which(sapply(g7_models
                                      , function(x) x$maturity==as.numeric(asset["maturity"])))
                         , function(x) {
                           g7_models[[x]] 
                         })
  
  model_res <- lapply(asset_models, function (m) {
    print(paste(asset["id"], m$name))
    y <- model_data[ , index_ret]
    X <- cbind(intercept=1, model_data[ , m$features, with=F])
    
    k_res <- sapply(seq(1, NUM_K_FOLDS), function(fold) {
      train_ix <- k_ix[k != fold, ix]
      test_ix <- k_ix[k == fold, ix]
      
      train_y <- y[train_ix]
      train_X <- X[train_ix]

      Aeq <- c(0, rep(1, ncol(train_X) - 1))
      beq <- 1
      lb <- c(caim::NEG_INF, rep(0, ncol(train_X) - 1))
      wts <- caim::d_exp(caim::halflife2decay(12), nrow(train_X))
  
      fixed_coefs <- NULL
      if (m$unity_coefs)
        fixed_coefs <- c(0, rep(1/length(m$features), length(m$features)))
      
      lmq <- caim::lm_quad(train_y, train_X
                           , Aeq=Aeq
                           , beq=beq
                           , lb=lb
                           , wts=wts
                           , fixed_coefs=fixed_coefs
                           )
      
      test_y <- y[test_ix]
      test_X <- X[test_ix]
      
      # print(test_X)
      test_y_hat <- as.matrix(test_X) %*% lmq$coefs
      residuals <- test_y_hat - test_y
      R2 <- cor(test_y, test_y_hat) ^ 2
      RMSE <- sqrt(mean(residuals^2))
      alpha_y <- (1 + mean(residuals)) ^ 12 - 1
      te_y <- sd(residuals) * sqrt(12)

      return(round(c(k=fold, R2=R2, RMSE=RMSE, alpha_y=alpha_y, te_y=te_y), 6))

    })
    
    return(data.table(asset=asset["id"], model=m$name, type=m$type, t(k_res)))

  })

  return(rbindlist(model_res))

}))[
  , .(
    R2_mean = mean(R2)
    , RMSE_mean = mean(RMSE)
    , alpha_y_mean = mean(alpha_y)
    , te_y_mean = mean(te_y)
    , R2_sd = sd(R2)
    , RMSE_sd = sd(RMSE)
    , alpha_y_sd = sd(alpha_y)
    , te_y_sd = sd(te_y)
    , score = mean(RMSE) * sd(RMSE)
  )
  , by=.(asset, model, type)
  ][
    g7_assets, on=c(asset="id")
  ][
    , .SD
    , key=.(country, maturity, score)
  ]
[1] "CA.GOVT.13 x_g13_coup_bullet"
[1] "CA.GOVT.13 x_g13_shift_bullet"
[1] "CA.GOVT.13 x_g13_lin_bullet"
[1] "CA.GOVT.13 x_g13_ns_bullet"
[1] "CA.GOVT.13 x_g13_coup_n"
[1] "CA.GOVT.13 x_g13_shift_n"
[1] "CA.GOVT.13 x_g13_lin_n"
[1] "CA.GOVT.13 x_g13_ns_n"
[1] "CA.GOVT.13 x_g13_coup_ladder"
[1] "CA.GOVT.13 x_g13_shift_ladder"
[1] "CA.GOVT.13 x_g13_lin_ladder"
[1] "CA.GOVT.13 x_g13_ns_ladder"
[1] "CA.GOVT.15 x_g15_coup_bullet"
[1] "CA.GOVT.15 x_g15_shift_bullet"
[1] "CA.GOVT.15 x_g15_lin_bullet"
[1] "CA.GOVT.15 x_g15_ns_bullet"
[1] "CA.GOVT.15 x_g15_coup_n"
[1] "CA.GOVT.15 x_g15_shift_n"
[1] "CA.GOVT.15 x_g15_lin_n"
[1] "CA.GOVT.15 x_g15_ns_n"
[1] "CA.GOVT.15 x_g15_coup_ladder"
[1] "CA.GOVT.15 x_g15_shift_ladder"
[1] "CA.GOVT.15 x_g15_lin_ladder"
[1] "CA.GOVT.15 x_g15_ns_ladder"
[1] "CA.GOVT.110 x_g110_coup_bullet"
[1] "CA.GOVT.110 x_g110_shift_bullet"
[1] "CA.GOVT.110 x_g110_lin_bullet"
[1] "CA.GOVT.110 x_g110_ns_bullet"
[1] "CA.GOVT.110 x_g110_coup_n"
[1] "CA.GOVT.110 x_g110_shift_n"
[1] "CA.GOVT.110 x_g110_lin_n"
[1] "CA.GOVT.110 x_g110_ns_n"
[1] "CA.GOVT.110 x_g110_coup_ladder"
[1] "CA.GOVT.110 x_g110_shift_ladder"
[1] "CA.GOVT.110 x_g110_lin_ladder"
[1] "CA.GOVT.110 x_g110_ns_ladder"
[1] "DE.GOVT.13 x_g13_coup_bullet"
[1] "DE.GOVT.13 x_g13_shift_bullet"
[1] "DE.GOVT.13 x_g13_lin_bullet"
[1] "DE.GOVT.13 x_g13_ns_bullet"
[1] "DE.GOVT.13 x_g13_coup_n"
[1] "DE.GOVT.13 x_g13_shift_n"
[1] "DE.GOVT.13 x_g13_lin_n"
[1] "DE.GOVT.13 x_g13_ns_n"
[1] "DE.GOVT.13 x_g13_coup_ladder"
[1] "DE.GOVT.13 x_g13_shift_ladder"
[1] "DE.GOVT.13 x_g13_lin_ladder"
[1] "DE.GOVT.13 x_g13_ns_ladder"
[1] "DE.GOVT.15 x_g15_coup_bullet"
[1] "DE.GOVT.15 x_g15_shift_bullet"
[1] "DE.GOVT.15 x_g15_lin_bullet"
[1] "DE.GOVT.15 x_g15_ns_bullet"
[1] "DE.GOVT.15 x_g15_coup_n"
[1] "DE.GOVT.15 x_g15_shift_n"
[1] "DE.GOVT.15 x_g15_lin_n"
[1] "DE.GOVT.15 x_g15_ns_n"
[1] "DE.GOVT.15 x_g15_coup_ladder"
[1] "DE.GOVT.15 x_g15_shift_ladder"
[1] "DE.GOVT.15 x_g15_lin_ladder"
[1] "DE.GOVT.15 x_g15_ns_ladder"
[1] "DE.GOVT.110 x_g110_coup_bullet"
[1] "DE.GOVT.110 x_g110_shift_bullet"
[1] "DE.GOVT.110 x_g110_lin_bullet"
[1] "DE.GOVT.110 x_g110_ns_bullet"
[1] "DE.GOVT.110 x_g110_coup_n"
[1] "DE.GOVT.110 x_g110_shift_n"
[1] "DE.GOVT.110 x_g110_lin_n"
[1] "DE.GOVT.110 x_g110_ns_n"
[1] "DE.GOVT.110 x_g110_coup_ladder"
[1] "DE.GOVT.110 x_g110_shift_ladder"
[1] "DE.GOVT.110 x_g110_lin_ladder"
[1] "DE.GOVT.110 x_g110_ns_ladder"
[1] "FR.GOVT.13 x_g13_coup_bullet"
[1] "FR.GOVT.13 x_g13_shift_bullet"
[1] "FR.GOVT.13 x_g13_lin_bullet"
[1] "FR.GOVT.13 x_g13_ns_bullet"
[1] "FR.GOVT.13 x_g13_coup_n"
[1] "FR.GOVT.13 x_g13_shift_n"
[1] "FR.GOVT.13 x_g13_lin_n"
[1] "FR.GOVT.13 x_g13_ns_n"
[1] "FR.GOVT.13 x_g13_coup_ladder"
[1] "FR.GOVT.13 x_g13_shift_ladder"
[1] "FR.GOVT.13 x_g13_lin_ladder"
[1] "FR.GOVT.13 x_g13_ns_ladder"
[1] "FR.GOVT.15 x_g15_coup_bullet"
[1] "FR.GOVT.15 x_g15_shift_bullet"
[1] "FR.GOVT.15 x_g15_lin_bullet"
[1] "FR.GOVT.15 x_g15_ns_bullet"
[1] "FR.GOVT.15 x_g15_coup_n"
[1] "FR.GOVT.15 x_g15_shift_n"
[1] "FR.GOVT.15 x_g15_lin_n"
[1] "FR.GOVT.15 x_g15_ns_n"
[1] "FR.GOVT.15 x_g15_coup_ladder"
[1] "FR.GOVT.15 x_g15_shift_ladder"
[1] "FR.GOVT.15 x_g15_lin_ladder"
[1] "FR.GOVT.15 x_g15_ns_ladder"
[1] "FR.GOVT.110 x_g110_coup_bullet"
[1] "FR.GOVT.110 x_g110_shift_bullet"
[1] "FR.GOVT.110 x_g110_lin_bullet"
[1] "FR.GOVT.110 x_g110_ns_bullet"
[1] "FR.GOVT.110 x_g110_coup_n"
[1] "FR.GOVT.110 x_g110_shift_n"
[1] "FR.GOVT.110 x_g110_lin_n"
[1] "FR.GOVT.110 x_g110_ns_n"
[1] "FR.GOVT.110 x_g110_coup_ladder"
[1] "FR.GOVT.110 x_g110_shift_ladder"
[1] "FR.GOVT.110 x_g110_lin_ladder"
[1] "FR.GOVT.110 x_g110_ns_ladder"
[1] "IT.GOVT.13 x_g13_coup_bullet"
[1] "IT.GOVT.13 x_g13_shift_bullet"
[1] "IT.GOVT.13 x_g13_lin_bullet"
[1] "IT.GOVT.13 x_g13_ns_bullet"
[1] "IT.GOVT.13 x_g13_coup_n"
[1] "IT.GOVT.13 x_g13_shift_n"
[1] "IT.GOVT.13 x_g13_lin_n"
[1] "IT.GOVT.13 x_g13_ns_n"
[1] "IT.GOVT.13 x_g13_coup_ladder"
[1] "IT.GOVT.13 x_g13_shift_ladder"
[1] "IT.GOVT.13 x_g13_lin_ladder"
[1] "IT.GOVT.13 x_g13_ns_ladder"
[1] "IT.GOVT.15 x_g15_coup_bullet"
[1] "IT.GOVT.15 x_g15_shift_bullet"
[1] "IT.GOVT.15 x_g15_lin_bullet"
[1] "IT.GOVT.15 x_g15_ns_bullet"
[1] "IT.GOVT.15 x_g15_coup_n"
[1] "IT.GOVT.15 x_g15_shift_n"
[1] "IT.GOVT.15 x_g15_lin_n"
[1] "IT.GOVT.15 x_g15_ns_n"
[1] "IT.GOVT.15 x_g15_coup_ladder"
[1] "IT.GOVT.15 x_g15_shift_ladder"
[1] "IT.GOVT.15 x_g15_lin_ladder"
[1] "IT.GOVT.15 x_g15_ns_ladder"
[1] "IT.GOVT.110 x_g110_coup_bullet"
[1] "IT.GOVT.110 x_g110_shift_bullet"
[1] "IT.GOVT.110 x_g110_lin_bullet"
[1] "IT.GOVT.110 x_g110_ns_bullet"
[1] "IT.GOVT.110 x_g110_coup_n"
[1] "IT.GOVT.110 x_g110_shift_n"
[1] "IT.GOVT.110 x_g110_lin_n"
[1] "IT.GOVT.110 x_g110_ns_n"
[1] "IT.GOVT.110 x_g110_coup_ladder"
[1] "IT.GOVT.110 x_g110_shift_ladder"
[1] "IT.GOVT.110 x_g110_lin_ladder"
[1] "IT.GOVT.110 x_g110_ns_ladder"
[1] "JP.GOVT.13 x_g13_coup_bullet"
[1] "JP.GOVT.13 x_g13_shift_bullet"
[1] "JP.GOVT.13 x_g13_lin_bullet"
[1] "JP.GOVT.13 x_g13_ns_bullet"
[1] "JP.GOVT.13 x_g13_coup_n"
[1] "JP.GOVT.13 x_g13_shift_n"
[1] "JP.GOVT.13 x_g13_lin_n"
[1] "JP.GOVT.13 x_g13_ns_n"
[1] "JP.GOVT.13 x_g13_coup_ladder"
[1] "JP.GOVT.13 x_g13_shift_ladder"
[1] "JP.GOVT.13 x_g13_lin_ladder"
[1] "JP.GOVT.13 x_g13_ns_ladder"
[1] "JP.GOVT.15 x_g15_coup_bullet"
[1] "JP.GOVT.15 x_g15_shift_bullet"
[1] "JP.GOVT.15 x_g15_lin_bullet"
[1] "JP.GOVT.15 x_g15_ns_bullet"
[1] "JP.GOVT.15 x_g15_coup_n"
[1] "JP.GOVT.15 x_g15_shift_n"
[1] "JP.GOVT.15 x_g15_lin_n"
[1] "JP.GOVT.15 x_g15_ns_n"
[1] "JP.GOVT.15 x_g15_coup_ladder"
[1] "JP.GOVT.15 x_g15_shift_ladder"
[1] "JP.GOVT.15 x_g15_lin_ladder"
[1] "JP.GOVT.15 x_g15_ns_ladder"
[1] "JP.GOVT.110 x_g110_coup_bullet"
[1] "JP.GOVT.110 x_g110_shift_bullet"
[1] "JP.GOVT.110 x_g110_lin_bullet"
[1] "JP.GOVT.110 x_g110_ns_bullet"
[1] "JP.GOVT.110 x_g110_coup_n"
[1] "JP.GOVT.110 x_g110_shift_n"
[1] "JP.GOVT.110 x_g110_lin_n"
[1] "JP.GOVT.110 x_g110_ns_n"
[1] "JP.GOVT.110 x_g110_coup_ladder"
[1] "JP.GOVT.110 x_g110_shift_ladder"
[1] "JP.GOVT.110 x_g110_lin_ladder"
[1] "JP.GOVT.110 x_g110_ns_ladder"
[1] "UK.GOVT.13 x_g13_coup_bullet"
[1] "UK.GOVT.13 x_g13_shift_bullet"
[1] "UK.GOVT.13 x_g13_lin_bullet"
[1] "UK.GOVT.13 x_g13_ns_bullet"
[1] "UK.GOVT.13 x_g13_coup_n"
[1] "UK.GOVT.13 x_g13_shift_n"
[1] "UK.GOVT.13 x_g13_lin_n"
[1] "UK.GOVT.13 x_g13_ns_n"
[1] "UK.GOVT.13 x_g13_coup_ladder"
[1] "UK.GOVT.13 x_g13_shift_ladder"
[1] "UK.GOVT.13 x_g13_lin_ladder"
[1] "UK.GOVT.13 x_g13_ns_ladder"
[1] "UK.GOVT.15 x_g15_coup_bullet"
[1] "UK.GOVT.15 x_g15_shift_bullet"
[1] "UK.GOVT.15 x_g15_lin_bullet"
[1] "UK.GOVT.15 x_g15_ns_bullet"
[1] "UK.GOVT.15 x_g15_coup_n"
[1] "UK.GOVT.15 x_g15_shift_n"
[1] "UK.GOVT.15 x_g15_lin_n"
[1] "UK.GOVT.15 x_g15_ns_n"
[1] "UK.GOVT.15 x_g15_coup_ladder"
[1] "UK.GOVT.15 x_g15_shift_ladder"
[1] "UK.GOVT.15 x_g15_lin_ladder"
[1] "UK.GOVT.15 x_g15_ns_ladder"
[1] "UK.GOVT.110 x_g110_coup_bullet"
[1] "UK.GOVT.110 x_g110_shift_bullet"
[1] "UK.GOVT.110 x_g110_lin_bullet"
[1] "UK.GOVT.110 x_g110_ns_bullet"
[1] "UK.GOVT.110 x_g110_coup_n"
[1] "UK.GOVT.110 x_g110_shift_n"
[1] "UK.GOVT.110 x_g110_lin_n"
[1] "UK.GOVT.110 x_g110_ns_n"
[1] "UK.GOVT.110 x_g110_coup_ladder"
[1] "UK.GOVT.110 x_g110_shift_ladder"
[1] "UK.GOVT.110 x_g110_lin_ladder"
[1] "UK.GOVT.110 x_g110_ns_ladder"
[1] "US.GOVT.13 x_g13_coup_bullet"
[1] "US.GOVT.13 x_g13_shift_bullet"
[1] "US.GOVT.13 x_g13_lin_bullet"
[1] "US.GOVT.13 x_g13_ns_bullet"
[1] "US.GOVT.13 x_g13_coup_n"
[1] "US.GOVT.13 x_g13_shift_n"
[1] "US.GOVT.13 x_g13_lin_n"
[1] "US.GOVT.13 x_g13_ns_n"
[1] "US.GOVT.13 x_g13_coup_ladder"
[1] "US.GOVT.13 x_g13_shift_ladder"
[1] "US.GOVT.13 x_g13_lin_ladder"
[1] "US.GOVT.13 x_g13_ns_ladder"
[1] "US.GOVT.15 x_g15_coup_bullet"
[1] "US.GOVT.15 x_g15_shift_bullet"
[1] "US.GOVT.15 x_g15_lin_bullet"
[1] "US.GOVT.15 x_g15_ns_bullet"
[1] "US.GOVT.15 x_g15_coup_n"
[1] "US.GOVT.15 x_g15_shift_n"
[1] "US.GOVT.15 x_g15_lin_n"
[1] "US.GOVT.15 x_g15_ns_n"
[1] "US.GOVT.15 x_g15_coup_ladder"
[1] "US.GOVT.15 x_g15_shift_ladder"
[1] "US.GOVT.15 x_g15_lin_ladder"
[1] "US.GOVT.15 x_g15_ns_ladder"
[1] "US.GOVT.110 x_g110_coup_bullet"
[1] "US.GOVT.110 x_g110_shift_bullet"
[1] "US.GOVT.110 x_g110_lin_bullet"
[1] "US.GOVT.110 x_g110_ns_bullet"
[1] "US.GOVT.110 x_g110_coup_n"
[1] "US.GOVT.110 x_g110_shift_n"
[1] "US.GOVT.110 x_g110_lin_n"
[1] "US.GOVT.110 x_g110_ns_n"
[1] "US.GOVT.110 x_g110_coup_ladder"
[1] "US.GOVT.110 x_g110_shift_ladder"
[1] "US.GOVT.110 x_g110_lin_ladder"
[1] "US.GOVT.110 x_g110_ns_ladder"
k_fold_best_models <- k_fold_eval[
  , head(.SD, 3)
  , by=.(country, maturity, asset)
  ]

best_model_freq <- sort(table(k_fold_best_models$type), decreasing=T)
best_model_freq

       lin_n   lin_ladder    ns_ladder shift_ladder      shift_n   lin_bullet    ns_bullet         ns_n 
          18           16            9            8            7            3            1            1 

Old appendix: constrained linear least squares = quadratic programming

t(sapply(names(g13_models), function(x) {
  model <- g13_models[[x]]
  y <- model_data[ , index_ret]
  X <- cbind(intercept=1, model_data[ , model$features, with=F])

  Aeq <- c(0, rep(1, ncol(X) - 1))
  beq <- 1
  lb <- c(caim::NEG_INF, rep(0, ncol(X) - 1))
  wts <- caim::d_exp(caim::halflife2decay(12), nrow(X))
  
  fixed_coefs <- NULL
  if (model$unity_coefs)
    fixed_coefs <- c(0, rep(1/length(model$features), length(model$features)))

  lmq <- caim::lm_quad(y, X, Aeq=Aeq, beq=beq, lb=lb, wts=wts, fixed_coefs=fixed_coefs)
  # coefs <- round(lmq$coefs, 6)
  # print(paste(x, coefs))
  
  print(lmq$coefs)
  return(round(evaluate(lmq), 6))
  # return(as.matrix(c(model=x, t(round(evaluate(lmq), 6)))))
}))
LS0tCnRpdGxlOiAiRzcgR292ZXJubWVudCBZaWVsZHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBJbml0aWFsaXplIGVudmlyb25tZW50CmBgYHtyfQpsaWJyYXJ5KGNhaW0pCmNhaW06OmNscigpCmBgYAoKIyMjIFNldHVwIHByZWZlcmVuY2VzCmBgYHtyfQojIG1vc3QgaW1wb3J0YW50IG1hdHVyaXRpZXMKbWF0dXJpdGllc19hID0gYyguMjUsIDIsIDUsIDEwKQojIGludGVyZXN0aW5nIG1hdHVyaXRpZXMsIGxldmVsIGIKbWF0dXJpdGllc19iID0gYyguMDgzMywgMSwgMzApCiMgYWxsIG90aGVyIG1hdHVyaXRpZXMgd2lsbCBiZSBsZXZlbCBjCgojIHJlbGF0aXZlIHdlaWdodGluZ3MgZm9yIGVhY2ggbWF0dXJpdHkgbGV2ZWwKIyB0aGVzZSB3aWxsIGZvcm0gYSBkZW5zaXR5IGZ1bmN0aW9uIHRoYXQgd2lsbCBiZSBub3JtYWxpc2VkIHRvIHN1bSB0byAxCiMgICAsIHNvIGRvbid0IHdvcnJ5IGFib3V0IHRoYXQgaGVyZQpNQVRfQV9XVCA9IDEKTUFUX0JfV1QgPSAuNzUKTUFUX0NfV1QgPSAwLjE1CgojIHNldCBvZiBtYXR1cml0aWVzIHRvIHVzZSBmb3IgbGFtYmRhIHZhbHVlcwpsYW1iZGFfbWF0dXJpdGllcyA8LSBzZXEoMC41LCA1LCAwLjUpCgojIGsgZm9yIGstZm9sZCBldmFsdWF0aW9uCk5VTV9LX0ZPTERTID0gMTAKCmBgYAoKIyMjIExvYWQgRzcgeWllbGQgaW5mbwpgYGB7cn0KZzdfcmF0ZXNfaW5mbyA8LSBmcmVhZCgiLi4vLi4vZGF0YS9yYXRlc2luZm8uY3N2IilbCiAgY291bnRyeSAlaW4lIGMoIlVTIiwgIkNBIiwgIkRFIiwgIkZSIiwgIklUIiwgIlVLIiwgIkpQIikgJiBjbGFzcz09IkdPVlQiCiAgLCAuKGNvdW50cnksIGNsYXNzLCBtYXR1cml0eSwgaWQ9YmJfdGlja2VyKQpdCgpnN19yYXRlc19kYXRhIDwtIGc3X3JhdGVzX2luZm9bCiAgZnJlYWQoIi4uLy4uL2RhdGEvcmF0ZXNkYXRhLmNzdiIpCiAgLCAKICAsIG9uPS4oaWQpLCBub21hdGNoPU5VTEwgCl1bCiAgLCAuKGNvdW50cnkKICAgICAgLCBjbGFzcwogICAgICAsIG1hdHVyaXR5CiAgICAgICwgZGF0ZT1sdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkKICAgICAgLCB5ZWFyPWx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkpCiAgICAgICwgbW9udGg9bHVicmlkYXRlOjptb250aChsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkpCiAgICAgICwgeWllbGQ9UFhfTEFTVAogICAgICApCl1bCiAgLCAuU0QsIGtleT0uKGNvdW50cnksIGNsYXNzLCBtYXR1cml0eSwgZGF0ZSkKXQoKCmc3X2N1cnZlX21hdHMgPC0gc29ydCh1bmlxdWUoZzdfcmF0ZXNfZGF0YSRtYXR1cml0eSkpCmc3X2N1cnZlX2NvbHVtbnMgPC0gcGFzdGUwKCJ5aWVsZF8iLCBnN19jdXJ2ZV9tYXRzKQoKZzdfY3VydmVzIDwtIGRjYXN0KAogIGc3X3JhdGVzX2RhdGEsIGNvdW50cnkgKyBjbGFzcyArIGRhdGUgKyB5ZWFyICsgbW9udGggfiBtYXR1cml0eSwgdmFsdWUudmFyPSJ5aWVsZCIgIAopWwogICwgLlNECiAgLCBrZXk9Lihjb3VudHJ5LCBjbGFzcywgZGF0ZSkgIApdCnNldG5hbWVzKGc3X2N1cnZlcywgYyhrZXkoZzdfY3VydmVzKSwgZzdfY3VydmVfbWF0cyksIGMoa2V5KGc3X2N1cnZlcyksIGc3X2N1cnZlX2NvbHVtbnMpKQoKZzdfY3VydmVzW10KYGBgCgojIyMgTG9hZCBHNyBNb25leSBtYXJrZXQgZGF0YQpgYGB7cn0KZzdfbW1faW5mbyA8LSBkYXRhLnRhYmxlKAogIGNvdW50cnk9YygiVVMiLCAiQ0EiLCAiREUiLCAiRlIiLCAiSVQiLCAiVUsiLCAiSlAiLCAiREUiLCAiRlIiLCAiSVQiKQogICwgY3VybmN5PWMoIlVTRCIsICJDQUQiLCAiRVVSIiwgIkVVUiIsICJFVVIiLCAiR0JQIiwgIkpQWSIsICJERU0iLCAiRlJGIiwgIklUTCIpCiAgLCBpZD1jKCJVU0QuRklYLjFNIiwgIkNBRC5GSVguMU0iLCAiRVVSLkZJWC4xTSIsICJFVVIuRklYLjFNIiwgIkVVUi5GSVguMU0iCiAgICAgICAgICwgIkdCUC5GSVguMU0iLCAiSlBZLkZJWC4xTSIsICJERU0uRklYLjFNIiwgIkZSRi5GSVguMU0iLCAiSVRMLkZJWC4xTSIpCikKIyBnN19tbV9pbmZvIDwtIGZyZWFkKCIuLi9kYXRhL21taW5mby5jc3YiKVsKIyAgIENVUk5DWSAlaW4lIGMoIlVTRCIsICAiRVVSIiwgIkdCUCIsICJKUFkiLCAiQ0FEIikKIyAgICwgLihjdXJuY3k9Q1VSTkNZLCBpZD1GSVhfMU0pCiMgXQpnN19tbV9kYXRhIDwtIGc3X21tX2luZm9bCiAgZnJlYWQoIi4uLy4uL2RhdGEvbW1kYXRhLmNzdiIpCiAgLCAKICAsIG9uPS4oaWQpLCBub21hdGNoPU5VTEwKXVsKICAsIC4oY291bnRyeQogICAgICAsIGN1cm5jeQogICAgICAsIGRhdGUgPSBsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkKICAgICAgLCB5ZWFyPWx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkpCiAgICAgICwgbW9udGg9bHVicmlkYXRlOjptb250aChsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkpCiAgICAgICwgbWF0dXJpdHkgPSAwLjgzMzMKICAgICAgLCB5aWVsZCA9IFBYX0xBU1QKICAgICAgKQpdWwogICwgLlNEICwga2V5PS4oY291bnRyeSwgY3VybmN5LCBkYXRlKQpdCgojIHN0aXRjaCB0b2dldGhlciBwcmUtIGFuZCBwb3N0LUVVUiBjb252ZXJnZW5jZSBkYXRhIGZvciBERSwgRlIsIElUCmV1cl9zdGFydF9kYXRlIDwtIG1pbihnN19tbV9kYXRhW2NvdW50cnk9PSJERSIgJiBjdXJuY3k9PSJFVVIiXSRkYXRlKQojIGRlbGV0ZSBsb2NhbCBvYnNlcnZhdGlvbnMgd2hlcmUgRVVSIGRhdGEgaXMgYXZhaWxhYmxlCmc3X21tX2RhdGEgPC0gZzdfbW1fZGF0YVshKGN1cm5jeSAlaW4lIGMoIkRFTSIsICJGUkYiLCAiSVRMIikgJiBkYXRlID49IGV1cl9zdGFydF9kYXRlKV0KIyByZWxhYmVsIHByZS1FVVIgbG9jYWwgb2JzZXJ2YXRpb25zIGFzIEVVUgojIG1heSBuZWVkIHRvIHJldGhpbmsgdGhpcyBpZiB3ZSB3YW50IHRvIGltcGxlbWVudCBwcmUtIGFuZCBwb3N0LUVVUiBjdXJyZW5jeSBkYXRhCiMgLSBtYXliZSBsYWJlbCBldmVyeXRoaW5nIGFzIERFTSBGUkYgSVRMPwpnN19tbV9kYXRhW2N1cm5jeSAlaW4lIGMoIkRFTSIsICJGUkYiLCAiSVRMIiksIGN1cm5jeSA6PSAiRVVSIl0gCgpnN19tbV9kYXRhW10KYGBgCgojIyMgQ3JlYXRlIG1vbnRobHkgZGF0YQpgYGB7cn0KZzdfY3VydmVzX20gPC0gZzdfY3VydmVzWwogIGRhdGUgJWluJSBjYWltOjptb250aF9lbmRfZGF0ZXMoZGF0ZSkKXQoKZzdfbW1fZGF0YV9tIDwtIGc3X21tX2RhdGFbCiAgZGF0ZSAlaW4lIGNhaW06Om1vbnRoX2VuZF9kYXRlcyhkYXRlKQpdCgpgYGAKCiMjIyBBZGQgbW9uZXkgbWFya2V0IGRhdGEgdG8gc2hvcnQgZW5kIG9mIGdvdmVybm1lbnQgY3VydmVzClRoaXMgaXMsIGFkbWl0dGVkbHksIGEgYmlnIGtsdWRnZSwgYnV0IHNob3VsZCBoZWxwIHdoZW4gdGhlcmUgYXJlIG5vIGdvdmVybm1lbnQgeWllbGRzIGJlbG93CjIteWVhciBtYXR1cml0eSwgbGlrZSBlYXJseSBDYW5hZGEsIGV0Yy4gQWxzbywgdGhpcyBtYXkgYmUgYSBnb29kIHdheSB0byBhdmVyYWdlIDMtbW9udGggYmlsbHMgYW5kIAoxLW1vbnRoIGRlcG9zaXRzIGFzIGEgcHJveHkgZm9yIGNhc2ggd2hlbiBkb2luZyBOZWxzb24tU2llZ2VsIGFuYWx5c2VzLgoKQXNzdW1wdGlvbjogV2UnbGwgZG8gTElCT1IgLSAxLzggYXMgYSBjcnVkZSBwcm94eSBmb3IgTElCSUQKYGBge3J9Cmc3X2N1cnZlc19tW2c3X21tX2RhdGFfbSwgeWllbGRfMC4wODMzIDo9IGkueWllbGQgLSAwLjEyNSwgb249Lihjb3VudHJ5LCB5ZWFyLCBtb250aCldW10KYGBgCgojIyMgRW5zdXJlIHRoZXJlIGFyZSBhdCBsZWFzdCAzIHlpZWxkIHBvaW50cwpgYGB7cn0KIyBlbnN1cmUgdGhlcmUgYXJlIGF0IGxlYXN0IDMgeWllbGQgcG9pbnRzCmc3X2N1cnZlc19tIDwtIGc3X2N1cnZlc19tWwogIGc3X2N1cnZlc19tWyAsLih2YWxpZD0oc3VtKCFpcy5uYSguU0QpKSA+PSAzKSksIGJ5PS4oY291bnRyeSwgY2xhc3MsIGRhdGUpXSR2YWxpZApdWwogICMgZmluZCBmaXJzdCBkYXRlIHRoYXQgd29ya3MgZm9yIGFsbCBjb3VudHJpZXMKICBkYXRlID49IG1heChnN19jdXJ2ZXNfbVssLihtaW5fZGF0ZT1taW4oZGF0ZSkpLCBieT0uKGNvdW50cnksIGNsYXNzKV0kbWluX2RhdGUpCl0KYGBgCgojIyMgQ2FsY3VsYXRlIE5lbHNvbiBTaWVnZWwgY29lZmZpY2llbnRzCmBgYHtyfQpjdXJ2ZV9kYXRhIDwtIGc3X2N1cnZlc19tCmN1cnZlX21hdHMgPC0gZzdfY3VydmVfbWF0cwpjdXJ2ZV9jb2x1bW5zIDwtIGc3X2N1cnZlX2NvbHVtbnMKCmN1cnZlX2NvZWZzIDwtIGxpc3QoKQpjdXJ2ZV9pZHMgPC0gdW5pcXVlKGN1cnZlX2RhdGFbLC4oY291bnRyeSwgY2xhc3MpXSkKZm9yIChjIGluIDE6bnJvdyhjdXJ2ZV9pZHMpKSB7CiAgdF9jdXJ2ZV9kYXRhIDwtIGN1cnZlX2RhdGFbY291bnRyeSA9PSBjdXJ2ZV9pZHNbYywgY291bnRyeV0gJiBjbGFzcyA9PSBjdXJ2ZV9pZHNbYywgY2xhc3NdXQogIHRfY3VydmVfZGF0ZXMgPC0gc29ydCh1bmlxdWUodF9jdXJ2ZV9kYXRhJGRhdGUpKQogIHRfY3VydmVfbWF0cyA8LSBjdXJ2ZV9tYXRzICNzb3J0KHVuaXF1ZSh5aWVsZF9pbmZvW2N1cnZlX2lkPT1jXSRtYXR1cml0eSkpCiAgdF9jdXJ2ZV9tYXRfbmFtZXMgPC0gY3VydmVfY29sdW1ucyAjYXMuY2hhcmFjdGVyKHRfY3VydmVfbWF0cykKICAKICB0X2N1cnZlX21hdF93dHMgPC0gcmVwKE1BVF9DX1dULCBsZW5ndGgodF9jdXJ2ZV9tYXRzKSkKICBuYW1lcyh0X2N1cnZlX21hdF93dHMpIDwtIHRfY3VydmVfbWF0X25hbWVzCiAgdF9jdXJ2ZV9tYXRfd3RzW25hbWVzKHRfY3VydmVfbWF0X3d0cykgJWluJSBtYXR1cml0aWVzX2FdIDwtIE1BVF9BX1dUCiAgdF9jdXJ2ZV9tYXRfd3RzW25hbWVzKHRfY3VydmVfbWF0X3d0cykgJWluJSBtYXR1cml0aWVzX2JdIDwtIE1BVF9CX1dUCiAgdF9jdXJ2ZV9tYXRfd3RzIDwtIHRfY3VydmVfbWF0X3d0cyAvIHN1bSh0X2N1cnZlX21hdF93dHMpCiAgCiAgCiAgdF9yZXMgPC0gbGlzdCgpCiAgZm9yIChpIGluIDE6bGVuZ3RoKGxhbWJkYV9tYXR1cml0aWVzKSkgewogICAgbWF0IDwtIGxhbWJkYV9tYXR1cml0aWVzW2ldCiAgICBsYW1iZGEgPC0gY2FpbTo6bnNfbWF0MmxhbWJkYShtYXQpCiAgICBwcmludChwYXN0ZSgiY2FsY3VsYXRpbmcgZm9yIGN1cnZlOiIsIGMsIGN1cnZlX2lkc1tjLCBjb3VudHJ5XSwgY3VydmVfaWRzW2MsIGNsYXNzXSwgIm1hdHVyaXR5OiIsIG1hdCwgImxhbWJkYToiLCBsYW1iZGEpKQogICAgdF9yZXNbW2ldXSA8LSBsaXN0KAogICAgICBtYXQ9bWF0CiAgICAgICwgbGFtYmRhPWxhbWJkYQogICAgICAsIGNvZWZzPWRhdGEudGFibGUoCiAgICAgICAgY291bnRyeT1jdXJ2ZV9pZHNbYywgY291bnRyeV0KICAgICAgICAsIGNsYXNzPWN1cnZlX2lkc1tjLCBjbGFzc10KICAgICAgICAsIGRhdGU9dF9jdXJ2ZV9kYXRhWyxkYXRlXQogICAgICAgICwgdChhcHBseSh0X2N1cnZlX2RhdGEsIDEsIGZ1bmN0aW9uKHgpIAogICAgICAgICAgY2FpbTo6bnNfeWllbGRzMmNvZWZzKHRfY3VydmVfbWF0cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgYXMubnVtZXJpYyh4W3RfY3VydmVfbWF0X25hbWVzXSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIGxhbWJkYT1sYW1iZGEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHd0cz10X2N1cnZlX21hdF93dHMpKSkKICAgICAgICAsIGxhbWJkYV9tYXQ9bWF0CiAgICAgICAgKQogICAgICApCiAgfQogIAogICMgY2FsY3VsYXRlIHN1bW1hcmllcwogICMgNSB5ZWFyIGhhbGZsaWZlIGFzc3VtaW5nIDI2MCBidXNpbmVzcyBkYXlzIGluIGEgeWVhcgogIGRlY2F5IDwtIGhhbGZsaWZlMmRlY2F5KDUqMjYwKQogIGxhbWJkYV9zdW1tYXJ5IDwtIGRhdGEudGFibGUodChzYXBwbHkodF9yZXMsIGZ1bmN0aW9uKHgpIAogICAgYyhtYXQ9eCRtYXQsIGxhbWJkYT14JGxhbWJkYSwgd3NzPW1lYW4oeCRjb2VmcyR3c3MpLCB3c3NfZXhwPW1lYW5fZXhwKHgkY29lZnMkd3NzLCBkZWNheSkpKSkpCgogICMgY2hvb3NlIGJlc3QgZGF0YQogIGJlc3RfaGlzdF9maXQgPC0gd2hpY2gobGFtYmRhX3N1bW1hcnkkd3NzID09IG1pbihsYW1iZGFfc3VtbWFyeSR3c3MpKQogIGJlc3RfZXhwX2ZpdCA8LSB3aGljaChsYW1iZGFfc3VtbWFyeSR3c3NfZXhwID09IG1pbihsYW1iZGFfc3VtbWFyeSR3c3NfZXhwKSkKICAjIHRha2UgYXZlcmFnZSBvZiBiZXN0IGZpdHMsIGJ1dCB0aWx0IHRvd2FyZHMgYmVzdF9leHBfZml0CiAgYmVzdF9peCA8LSBmbG9vcihtZWFuKGMoYmVzdF9oaXN0X2ZpdCwgYmVzdF9leHBfZml0KSkraWZlbHNlKGJlc3RfZXhwX2ZpdCA+IGJlc3RfaGlzdF9maXQsIDAuNSwgMCkpCiAgYmVzdF9oaXN0X2NvZWZzIDwtIHRfcmVzW1tiZXN0X2l4XV0kY29lZnMKICAjIGJlc3RfaGlzdF9jb2VmcyRjdXJ2ZV9pZCA8LSBjCgogICNhZGQgZGF0YSB0byBjdXJ2ZV9jb2VmcwogIGN1cnZlX2NvZWZzW1tsZW5ndGgoY3VydmVfY29lZnMpKzFdXSA8LSBiZXN0X2hpc3RfY29lZnMKfQoKbnNfY29lZnMgPC0gcmJpbmRsaXN0KGN1cnZlX2NvZWZzKVsKICAsIC4obnNfYmV0YTAgPSBiZXRhMAogICAgICAsIG5zX2JldGExID0gYmV0YTEKICAgICAgLCBuc19iZXRhMiA9IGJldGEyCiAgICAgICwgbnNfbGFtYmRhID0gbGFtYmRhCiAgICAgICwgbnNfbGFtYmRhX21hdCA9IGxhbWJkYV9tYXQKICAgICAgLCBuc193c3MgPSB3c3MKICAgICAgKQogICwga2V5PS4oY291bnRyeSwgY2xhc3MsIGRhdGUpCl0KCmc3X2N1cnZlc19tIDwtIGc3X2N1cnZlc19tW25zX2NvZWZzXQoKcm0obGlzdD1zZXRkaWZmKGxzKCksIGMoImc3X2N1cnZlcyIsICJnN19jdXJ2ZXNfbSIsICJnN19yYXRlc19kYXRhIiwgImc3X3JhdGVzX2luZm8iLCAiZzdfY3VydmVfY29sdW1ucyIsICJnN19jdXJ2ZV9tYXRzIikpKQpgYGAKCiMjIyBZaWVsZCBjYWxjdWxhdGlvbnMKYGBge3J9CmxvbmdfZGF0YSA8LSBtZWx0KAogIGc3X2N1cnZlc19tICAKICAsIGMoImNvdW50cnkiLCAiY2xhc3MiLCAiZGF0ZSIsICJuc19iZXRhMCIsICJuc19iZXRhMSIsICJuc19iZXRhMiIsICJuc19sYW1iZGEiKQogICwgIHBhdHRlcm5zKCJ5aWVsZF8iKQogICwgdmFsdWUubmFtZT0ieWllbGRfbm93IgopWwogICwgbWF0dXJpdHkgOj0gYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfcmVtb3ZlKHZhcmlhYmxlLCAieWllbGRfIikpCl1bCiAgLCAuKGNvdW50cnksIGNsYXNzLCBkYXRlLCBuc19iZXRhMCwgbnNfYmV0YTEsIG5zX2JldGEyLCBuc19sYW1iZGEsIG1hdHVyaXR5LCB5aWVsZF9ub3cpCl0KCiMgZmlsbCBpbiBtaXNzaW5nIHlpZWxkcyB3aXRoIGludGVycG9sYXRlZCBkYXRhCiMgdGFibGUgd2l0aCAhaXMubmEoeWllbGQpCnlpZWxkc192YWxpZCA8LSBsb25nX2RhdGFbIWlzLm5hKHlpZWxkX25vdyldWywgYygibWF0IiwgInlsZCIpIDo9IC4obWF0dXJpdHksIHlpZWxkX25vdyldCiMgdGFibGUgbWFwcGluZyB0byA8PQp5aWVsZHNfbG8gPC0geWllbGRzX3ZhbGlkW2xvbmdfZGF0YSwgLihjb3VudHJ5LCBkYXRlLCBtYXR1cml0eSwgbV9sbzA9bWF0LCB5X2xvMD15bGQpLCBvbj0uKGNvdW50cnksIGRhdGUsIG1hdHVyaXR5KSwgcm9sbD1UXQojIHRhYmxlIG1hcHBpbmcgdG8gPj0KeWllbGRzX2hpIDwtIHlpZWxkc192YWxpZFtsb25nX2RhdGEsIC4oY291bnRyeSwgZGF0ZSwgbWF0dXJpdHksIG1faGkwPW1hdCwgeV9oaTA9eWxkKSwgb249Lihjb3VudHJ5LCBkYXRlLCBtYXR1cml0eSksIHJvbGw9LUluZl0KCnlpZWxkc19saW4gPC0geWllbGRzX2hpWwogIHlpZWxkc19sbwogICwgLigKICAgIGNvdW50cnkKICAgICwgZGF0ZQogICAgLCBtYXR1cml0eQogICAgLCB5aWVsZF9saW4gPSBpZmVsc2UobV9oaTAgPT0gbV9sbzAKICAgICAgICAgICAgICAgICAgICAgICAgICwgeV9oaTAKICAgICAgICAgICAgICAgICAgICAgICAgICwgeV9sbzAgKyAobWF0dXJpdHkgLSBtX2xvMCkgKiAoeV9oaTAgLSB5X2xvMCkgLyAobV9oaTAgLSBtX2xvMCkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICwgeV9oaTAKICAgICwgeV9sbzAKICAgICwgbV9oaTAKICAgICwgbV9sbzAKICApCiAgLCBvbj0uKGNvdW50cnksIGRhdGUsIG1hdHVyaXR5KQpdCgpsb25nX2RhdGEgPC0gbG9uZ19kYXRhW3lpZWxkc19saW4sIG9uPS4oY291bnRyeSwgZGF0ZSwgbWF0dXJpdHkpXQoKbG9uZ19kYXRhIDwtIGxvbmdfZGF0YVsKICAsIGMoIm51bV9jb3VwcyIsICJtb2RfZHVyIikgOj0gLigKICAgIGlmZWxzZShtYXR1cml0eSA+IDAuMjUsIGlmZWxzZShjb3VudHJ5PT0iVVMiLCAyLCAxKSwgMCkKICAgICwgbWF0dXJpdHkKICApICAKXVsKICBtYXR1cml0eSA+IDAuMjUKICAsIG1vZF9kdXIgOj0gcm91bmQoY2FpbTo6bW9kaWZpZWRfZHVyYXRpb24oeWllbGRfbGluIC8gMTAwLCB5aWVsZF9saW4gLyAxMDAsIG1hdHVyaXR5LCBudW1fY291cHMpLCA2KQpdWwogICwgLigKICAgIG5zX2JldGEwCiAgICAsIG5zX2JldGExCiAgICAsIG5zX2JldGEyCiAgICAsIG5zX2xhbWJkYQogICAgLCBudW1fY291cHMKICAgICwgbW9kX2R1ciAjPSByb3VuZChjYWltOjptb2RpZmllZF9kdXJhdGlvbih5aWVsZF9ub3cgLyAxMDAsIHlpZWxkX25vdyAvIDEwMCwgbWF0dXJpdHksIDEpLCA2KQogICAgLCB5aWVsZF9ub3cKICAgICwgeWllbGRfbGluCiAgICAsIHlfaGkwCiAgICAsIHlfbG8wCiAgICAsIG1faGkwCiAgICAsIG1fbG8wCiAgICApCiAgLCBrZXk9Lihjb3VudHJ5LCBjbGFzcywgbWF0dXJpdHksIGRhdGUpCl1bCiAgLCB5aWVsZF9wcmV2IDo9IHNoaWZ0KHlpZWxkX2xpbiksIGJ5ID0gLihjb3VudHJ5LCBjbGFzcywgbWF0dXJpdHkpCl1bCiAgLCBjKCJ5X2hpMSIsICJ5X2xvMSIsICJtX2hpMSIsICJtX2xvMSIpIDo9IC4oCiAgICB5aWVsZF9saW4KICAgICwgc2hpZnQoeWllbGRfbGluKQogICAgLCBtYXR1cml0eQogICAgLCBzaGlmdChtYXR1cml0eSkKICApCiAgLCBieSA9IC4oY291bnRyeSwgY2xhc3MsIGRhdGUpCl1bCiAgLCB5aWVsZF9zZWxsX2xpbiA6PSAoCiAgICB5X2xvMSArICgobV9oaTEgLSAxLzEyKSAtIG1fbG8xKSAqICh5X2hpMSAtIHlfbG8xKSAvIChtX2hpMSAtIG1fbG8xKQogICkKXVsKICAsIHlpZWxkX2J1eV9ucyA6PSByb3VuZChjYWltOjpuc19jb2VmczJ5aWVsZHMoCiAgICBtYXRzID0gbWF0dXJpdHkKICAgICwgYmV0YTAgPSBzaGlmdChuc19iZXRhMCkKICAgICwgYmV0YTEgPSBzaGlmdChuc19iZXRhMSkKICAgICwgYmV0YTIgPSBzaGlmdChuc19iZXRhMikKICAgICwgbGFtYmRhID0gc2hpZnQobnNfbGFtYmRhKQogICkkeSwgNCkKICAsIGJ5ID0gLihjb3VudHJ5LCBjbGFzcywgbWF0dXJpdHkpCl1bCiAgLCB5aWVsZF9zZWxsX25zIDo9IHJvdW5kKGNhaW06Om5zX2NvZWZzMnlpZWxkcygKICAgIG1hdHMgPSBtYXR1cml0eSAtIDEvMgogICAgLCBiZXRhMCA9IG5zX2JldGEwCiAgICAsIGJldGExID0gbnNfYmV0YTEKICAgICwgYmV0YTIgPSBuc19iZXRhMgogICAgLCBsYW1iZGEgPSBuc19sYW1iZGEKICApJHksIDQpCl1bCiAgLCBjb3VwX2luY19saW4gOj0gcm91bmQoeWllbGRfcHJldiAvIDEyMDAsIDYpCl1bCiAgLCBzaGlmdF9pbmMgOj0gaWZlbHNlKG1hdHVyaXR5ID4gMC4yNSwgcm91bmQoKHlpZWxkX2xpbiAtIHlpZWxkX3ByZXYpIC8gMTAwICogLW1vZF9kdXIsIDYpLCAwKQpdWwogICwgdG90X3JldF9zaGlmdCA6PSBjb3VwX2luY19saW4gKyBzaGlmdF9pbmMKXVsKICAsIGR1cl9pbmNfbGluIDo9IGlmZWxzZSgKICAgIG1hdHVyaXR5ID4gMC4yNQogICAgLCByb3VuZChjYWltOjpib25kX3ByaWNlKHlpZWxkX3NlbGxfbGluIC8gMTAwLCB5aWVsZF9wcmV2IC8gMTAwLCBtYXR1cml0eSAtIDEvMTIsIDEsIDEpIC0gMSwgNikKICAgICwgMAogICAgKQpdWwogICwgdG90X3JldF9saW4gOj0gY291cF9pbmNfbGluICsgZHVyX2luY19saW4KXVsKICAsIGNvdXBfaW5jX25zIDo9IHJvdW5kKHlpZWxkX2J1eV9ucyAvIDEyMDAsIDYpICAKXVsKICwgcHJpY2VfaW5jX25zIDo9IGlmZWxzZSgKICAgbWF0dXJpdHkgPiAwLjI1CiAgICwgcm91bmQoY2FpbTo6Ym9uZF9wcmljZSh5aWVsZF9zZWxsX25zIC8gMTAwLCB5aWVsZF9idXlfbnMgLyAxMDAsIG1hdHVyaXR5IC0gMS8xMiwgMSwgMSkgLSAxLCA2KQogICAsIDAKICAgKQpdWwogICwgdG90X3JldF9ucyA6PSBjb3VwX2luY19ucyArIHByaWNlX2luY19ucwpdCgpsb25nX2RhdGFbY291bnRyeT09IlVTIl0KCndpZGVfZGF0YSA8LSBkY2FzdCgKICBsb25nX2RhdGEKICAsY291bnRyeSArIGNsYXNzICsgZGF0ZSArIG5zX2JldGEwICsgbnNfYmV0YTEgKyBuc19iZXRhMiArIG5zX2xhbWJkYSB+IG1hdHVyaXR5CiAgLCB2YWx1ZS52YXIgPSBuYW1lcyhsb25nX2RhdGEpWyEobmFtZXMobG9uZ19kYXRhKSAlaW4lIGMoImNvdW50cnkiLCAiY2xhc3MiLCAiZGF0ZSIsICJuc19iZXRhMCIsICJuc19iZXRhMSIsICJuc19iZXRhMiIsICJuc19sYW1iZGEiLCAibWF0dXJpdHkiKSldCgogICMgLCB2YWx1ZS52YXIgPSBjKCJtb2RfZHVyIiwgInlpZWxkX25vdyIsICJ5aWVsZF9wcmV2IiwgInlpZWxkX2J1eV9ucyIsICJ5aWVsZF9zZWxsX25zIgogICMgICAgICAgICAgICAgICAgICwgImNvdXBfaW5jX2xpbiIsICJkdXJfaW5jX2xpbiIsICJjb3VwX2luY19ucyIsICJwcmljZV9pbmNfbnMiLCAidG90X3JldF9ucyIKICAjICAgICAgICAgICAgICAgICApCiAgKQoKd2lkZV9kYXRhW10KCmc3X3JldHVybl9kYXRhIDwtIGRjYXN0KAogIGxvbmdfZGF0YVttYXR1cml0eSAlaW4lIGMoMC4wODMzLCAxLCAyLCAzLCA1LCA3LCAxMCldCiAgLCBjb3VudHJ5ICsgY2xhc3MgKyBkYXRlIH4gbWF0dXJpdHkKICAsIHZhbHVlLnZhciA9IGMoImNvdXBfaW5jX2xpbiIsICJ0b3RfcmV0X3NoaWZ0IiwgInRvdF9yZXRfbGluIiwgInRvdF9yZXRfbnMiKQopCgpnN19yZXR1cm5fZGF0YVtdCmBgYAoKIyMjIEdldCBhc3NldCBkYXRhCmBgYHtyfQpnN19hc3NldHMgPC0gZGF0YS50YWJsZSgKICBpZD1jKCJVUy5HT1ZULjEzIiwgIlVTLkdPVlQuMTUiLCAiVVMuR09WVC4xMTAiCiAgICAgICAsICJDQS5HT1ZULjEzIiwgIkNBLkdPVlQuMTUiLCAiQ0EuR09WVC4xMTAiCiAgICAgICAsICJERS5HT1ZULjEzIiwgIkRFLkdPVlQuMTUiLCAiREUuR09WVC4xMTAiCiAgICAgICAsICJGUi5HT1ZULjEzIiwgIkZSLkdPVlQuMTUiLCAiRlIuR09WVC4xMTAiCiAgICAgICAsICJJVC5HT1ZULjEzIiwgIklULkdPVlQuMTUiLCAiSVQuR09WVC4xMTAiCiAgICAgICAsICJVSy5HT1ZULjEzIiwgIlVLLkdPVlQuMTUiLCAiVUsuR09WVC4xMTAiCiAgICAgICAsICJKUC5HT1ZULjEzIiwgIkpQLkdPVlQuMTUiLCAiSlAuR09WVC4xMTAiCiAgICAgICApCiAgLCBjb3VudHJ5PWMocmVwKCJVUyIsIDMpLCByZXAoIkNBIiwgMyksIHJlcCgiREUiLCAzKSwgcmVwKCJGUiIsIDMpLCByZXAoIklUIiwgMykKICAgICAgICAgICAgICAsIHJlcCgiVUsiLCAzKSwgcmVwKCJKUCIsIDMpKQogICwgbWF0dXJpdHk9cmVwKGMoMTMsIDE1LCAxMTApLCA3KQogICwga2V5PWMoImNvdW50cnkiLCAibWF0dXJpdHkiKQopCmc3X2Fzc2V0X2luZm8gPC0gZnJlYWQoIi4uLy4uL2RhdGEvYXNzZXRpbmZvLmNzdiIpW3N1Z2duYW1lICVpbiUgZzdfYXNzZXRzJGlkXQoKZzdfYXNzZXRfaW5mb1tdCgpnN19hc3NldF9kYXRhIDwtIGc3X2Fzc2V0c1sKICBmcmVhZCgiLi4vLi4vZGF0YS9hc3NldGRhdGEuY3N2IikKICAsIC4oCiAgICBjb3VudHJ5CiAgICAsIG1hdHVyaXR5CiAgICAsIGlkCiAgICAsIGRhdGUgPSBsdWJyaWRhdGU6OmFzX2RhdGUoZGF0ZSkKICAgICwgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihkYXRlKQogICAgLCBtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoZGF0ZSkKICAgICwgaW5kZXhfbmF2PVBYX0xBU1QKICAgICkKICAsIG9uPS4oaWQpLCBub21hdGNoPU5VTEwKXVsKICBkYXRlICVpbiUgY2FpbTo6bW9udGhfZW5kX2RhdGVzKGRhdGUpCiAgLCAuU0QKICAsIGtleT0uKGNvdW50cnksIG1hdHVyaXR5LCBkYXRlKQpdWwogICwgaW5kZXhfcmV0IDo9IHJvdW5kKGluZGV4X25hdiAvIHNoaWZ0KGluZGV4X25hdikgLSAxLCA2KQogICwgYnk9Lihjb3VudHJ5LCBtYXR1cml0eSkKXQoKZzdfYXNzZXRfZGF0YVtdCmBgYAoKCiMjIyBNb2RlbCBTZXR1cApgYGB7cn0KZzdfbW9kZWxzIDwtIGxpc3QoCiAgbGlzdChuYW1lPSJ4X2cxM19jb3VwX2J1bGxldCIsIHR5cGU9ImNvdXBfYnVsbGV0IiwgbWF0dXJpdHk9MTMKICAgICAgICwgZmVhdHVyZXM9YygiY291cF9pbmNfbGluXzIiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTNfc2hpZnRfYnVsbGV0IiwgdHlwZT0ic2hpZnRfYnVsbGV0IiwgbWF0dXJpdHk9MTMKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X3NoaWZ0XzIiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTNfbGluX2J1bGxldCIsIHR5cGU9Imxpbl9idWxsZXQiLCBtYXR1cml0eT0xMwogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzIiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTNfbnNfYnVsbGV0IiwgdHlwZT0ibnNfYnVsbGV0IiwgbWF0dXJpdHk9MTMKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X25zXzIiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTNfY291cF9uIiwgdHlwZT0iY291cF9uIiwgbWF0dXJpdHk9MTMKICAgICAgICAgLCBmZWF0dXJlcz1jKCJjb3VwX2luY19saW5fMSIsICJjb3VwX2luY19saW5fMiIsICJjb3VwX2luY19saW5fMyIpLCB1bml0eV9jb2Vmcz1UKQogICwgbGlzdChuYW1lPSJ4X2cxM19zaGlmdF9uIiwgdHlwZT0ic2hpZnRfbiIsIG1hdHVyaXR5PTEzCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8xIiwgInRvdF9yZXRfc2hpZnRfMiIsICJ0b3RfcmV0X3NoaWZ0XzMiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTNfbGluX24iLCB0eXBlPSJsaW5fbiIsIG1hdHVyaXR5PTEzCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9saW5fMSIsICJ0b3RfcmV0X2xpbl8yIiwgInRvdF9yZXRfbGluXzMiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTNfbnNfbiIsIHR5cGU9Im5zX24iLCBtYXR1cml0eT0xMwogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbnNfMSIsICJ0b3RfcmV0X25zXzIiLCAidG90X3JldF9uc18zIiksIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzEzX2NvdXBfbGFkZGVyIiwgdHlwZT0iY291cF9sYWRkZXIiLCBtYXR1cml0eT0xMwogICAgICAgICAsIGZlYXR1cmVzPWMoImNvdXBfaW5jX2xpbl8xIiwgImNvdXBfaW5jX2xpbl8yIiwgImNvdXBfaW5jX2xpbl8zIiksIHVuaXR5X2NvZWZzPUYpCiAgLCBsaXN0KG5hbWU9InhfZzEzX3NoaWZ0X2xhZGRlciIsIHR5cGU9InNoaWZ0X2xhZGRlciIsIG1hdHVyaXR5PTEzCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8xIiwgInRvdF9yZXRfc2hpZnRfMiIsICJ0b3RfcmV0X3NoaWZ0XzMiKSwgdW5pdHlfY29lZnM9RikKICAsIGxpc3QobmFtZT0ieF9nMTNfbGluX2xhZGRlciIsIHR5cGU9Imxpbl9sYWRkZXIiLCBtYXR1cml0eT0xMwogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzEiLCAidG90X3JldF9saW5fMiIsICJ0b3RfcmV0X2xpbl8zIiksIHVuaXR5X2NvZWZzPUYpCiAgLCBsaXN0KG5hbWU9InhfZzEzX25zX2xhZGRlciIsIHR5cGU9Im5zX2xhZGRlciIsIG1hdHVyaXR5PTEzCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9uc18xIiwgInRvdF9yZXRfbnNfMiIsICJ0b3RfcmV0X25zXzMiKSwgdW5pdHlfY29lZnM9RikKCiAgLCBsaXN0KG5hbWU9InhfZzE1X2NvdXBfYnVsbGV0IiwgdHlwZT0iY291cF9idWxsZXQiLCBtYXR1cml0eT0xNQogICAgICAgICAsIGZlYXR1cmVzPWMoImNvdXBfaW5jX2xpbl8zIiksIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzE1X3NoaWZ0X2J1bGxldCIsIHR5cGU9InNoaWZ0X2J1bGxldCIsIG1hdHVyaXR5PTE1CiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8zIiksIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzE1X2xpbl9idWxsZXQiLCB0eXBlPSJsaW5fYnVsbGV0IiwgbWF0dXJpdHk9MTUKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X2xpbl8zIiksIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzE1X25zX2J1bGxldCIsIHR5cGU9Im5zX2J1bGxldCIsIG1hdHVyaXR5PTE1CiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9uc18zIiksIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzE1X2NvdXBfbiIsIHR5cGU9ImNvdXBfbiIsIG1hdHVyaXR5PTE1CiAgICAgICAgICwgZmVhdHVyZXM9YygiY291cF9pbmNfbGluXzEiLCAiY291cF9pbmNfbGluXzIiLCAiY291cF9pbmNfbGluXzMiLCAiY291cF9pbmNfbGluXzUiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTVfc2hpZnRfbiIsIHR5cGU9InNoaWZ0X24iLCBtYXR1cml0eT0xNQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfc2hpZnRfMSIsICJ0b3RfcmV0X3NoaWZ0XzIiLCAidG90X3JldF9zaGlmdF8zIiwgInRvdF9yZXRfc2hpZnRfNSIpLCB1bml0eV9jb2Vmcz1UKQogICwgbGlzdChuYW1lPSJ4X2cxNV9saW5fbiIsIHR5cGU9Imxpbl9uIiwgbWF0dXJpdHk9MTUKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X2xpbl8xIiwgInRvdF9yZXRfbGluXzIiLCAidG90X3JldF9saW5fMyIsICJ0b3RfcmV0X2xpbl81IiksIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzE1X25zX24iLCB0eXBlPSJuc19uIiwgbWF0dXJpdHk9MTUKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X25zXzEiLCAidG90X3JldF9uc18yIiwgInRvdF9yZXRfbnNfMyIsICJ0b3RfcmV0X25zXzUiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTVfY291cF9sYWRkZXIiLCB0eXBlPSJjb3VwX2xhZGRlciIsIG1hdHVyaXR5PTE1CiAgICAgICAgICwgZmVhdHVyZXM9YygiY291cF9pbmNfbGluXzEiLCAiY291cF9pbmNfbGluXzIiLCAiY291cF9pbmNfbGluXzMiLCAiY291cF9pbmNfbGluXzUiKSwgdW5pdHlfY29lZnM9RikKICAsIGxpc3QobmFtZT0ieF9nMTVfc2hpZnRfbGFkZGVyIiwgdHlwZT0ic2hpZnRfbGFkZGVyIiwgbWF0dXJpdHk9MTUKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X3NoaWZ0XzEiLCAidG90X3JldF9zaGlmdF8yIiwgInRvdF9yZXRfc2hpZnRfMyIsICJ0b3RfcmV0X3NoaWZ0XzUiKSwgdW5pdHlfY29lZnM9RikKICAsIGxpc3QobmFtZT0ieF9nMTVfbGluX2xhZGRlciIsIHR5cGU9Imxpbl9sYWRkZXIiLCBtYXR1cml0eT0xNQogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzEiLCAidG90X3JldF9saW5fMiIsICJ0b3RfcmV0X2xpbl8zIiwgInRvdF9yZXRfbGluXzUiKSwgdW5pdHlfY29lZnM9RikKICAsIGxpc3QobmFtZT0ieF9nMTVfbnNfbGFkZGVyIiwgdHlwZT0ibnNfbGFkZGVyIiwgbWF0dXJpdHk9MTUKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X25zXzEiLCAidG90X3JldF9uc18yIiwgInRvdF9yZXRfbnNfMyIsICJ0b3RfcmV0X25zXzUiKSwgdW5pdHlfY29lZnM9RikKICAKICAsIGxpc3QobmFtZT0ieF9nMTEwX2NvdXBfYnVsbGV0IiwgdHlwZT0iY291cF9idWxsZXQiLCBtYXR1cml0eT0xMTAKICAgICAgICAgLCBmZWF0dXJlcz1jKCJjb3VwX2luY19saW5fNSIpLCB1bml0eV9jb2Vmcz1UKQogICwgbGlzdChuYW1lPSJ4X2cxMTBfc2hpZnRfYnVsbGV0IiwgdHlwZT0ic2hpZnRfYnVsbGV0IiwgbWF0dXJpdHk9MTEwCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF81IiksIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzExMF9saW5fYnVsbGV0IiwgdHlwZT0ibGluX2J1bGxldCIsIG1hdHVyaXR5PTExMAogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbGluXzUiKSwgdW5pdHlfY29lZnM9VCkKICAsIGxpc3QobmFtZT0ieF9nMTEwX25zX2J1bGxldCIsIHR5cGU9Im5zX2J1bGxldCIsIG1hdHVyaXR5PTExMAogICAgICAgICAsIGZlYXR1cmVzPWMoInRvdF9yZXRfbnNfNSIpLCB1bml0eV9jb2Vmcz1UKQogICwgbGlzdChuYW1lPSJ4X2cxMTBfY291cF9uIiwgdHlwZT0iY291cF9uIiwgbWF0dXJpdHk9MTEwCiAgICAgICAgICwgZmVhdHVyZXM9YygiY291cF9pbmNfbGluXzEiLCAiY291cF9pbmNfbGluXzIiLCAiY291cF9pbmNfbGluXzMiLCAiY291cF9pbmNfbGluXzUiLCAiY291cF9pbmNfbGluXzciLCAiY291cF9pbmNfbGluXzEwIikKICAgICAgICAgLCB1bml0eV9jb2Vmcz1UKQogICwgbGlzdChuYW1lPSJ4X2cxMTBfc2hpZnRfbiIsIHR5cGU9InNoaWZ0X24iLCBtYXR1cml0eT0xMTAKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X3NoaWZ0XzEiLCAidG90X3JldF9zaGlmdF8yIiwgInRvdF9yZXRfc2hpZnRfMyIsICJ0b3RfcmV0X3NoaWZ0XzUiLCAidG90X3JldF9zaGlmdF83IiwgInRvdF9yZXRfc2hpZnRfMTAiKQogICAgICAgICAsIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzExMF9saW5fbiIsIHR5cGU9Imxpbl9uIiwgbWF0dXJpdHk9MTEwCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9saW5fMSIsICJ0b3RfcmV0X2xpbl8yIiwgInRvdF9yZXRfbGluXzMiLCAidG90X3JldF9saW5fNSIsICJ0b3RfcmV0X2xpbl83IiwgInRvdF9yZXRfbGluXzEwIikKICAgICAgICAgLCB1bml0eV9jb2Vmcz1UKQogICwgbGlzdChuYW1lPSJ4X2cxMTBfbnNfbiIsIHR5cGU9Im5zX24iLCBtYXR1cml0eT0xMTAKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X25zXzEiLCAidG90X3JldF9uc18yIiwgInRvdF9yZXRfbnNfMyIsICJ0b3RfcmV0X25zXzUiLCAidG90X3JldF9uc183IiwgInRvdF9yZXRfbnNfMTAiKQogICAgICAgICAsIHVuaXR5X2NvZWZzPVQpCiAgLCBsaXN0KG5hbWU9InhfZzExMF9jb3VwX2xhZGRlciIsIHR5cGU9ImNvdXBfbGFkZGVyIiwgbWF0dXJpdHk9MTEwCiAgICAgICAgICwgZmVhdHVyZXM9YygiY291cF9pbmNfbGluXzEiLCAiY291cF9pbmNfbGluXzIiLCAiY291cF9pbmNfbGluXzMiLCAiY291cF9pbmNfbGluXzUiLCAiY291cF9pbmNfbGluXzciLCAiY291cF9pbmNfbGluXzEwIikKICAgICAgICAgLCB1bml0eV9jb2Vmcz1GKQogICwgbGlzdChuYW1lPSJ4X2cxMTBfc2hpZnRfbGFkZGVyIiwgdHlwZT0ic2hpZnRfbGFkZGVyIiwgbWF0dXJpdHk9MTEwCiAgICAgICAgICwgZmVhdHVyZXM9YygidG90X3JldF9zaGlmdF8xIiwgInRvdF9yZXRfc2hpZnRfMiIsICJ0b3RfcmV0X3NoaWZ0XzMiLCAidG90X3JldF9zaGlmdF81IiwgInRvdF9yZXRfc2hpZnRfNyIsICJ0b3RfcmV0X3NoaWZ0XzEwIikKICAgICAgICAgLCB1bml0eV9jb2Vmcz1GKQogICwgbGlzdChuYW1lPSJ4X2cxMTBfbGluX2xhZGRlciIsIHR5cGU9Imxpbl9sYWRkZXIiLCBtYXR1cml0eT0xMTAKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X2xpbl8xIiwgInRvdF9yZXRfbGluXzIiLCAidG90X3JldF9saW5fMyIsICJ0b3RfcmV0X2xpbl81IiwgInRvdF9yZXRfbGluXzciLCAidG90X3JldF9saW5fMTAiKQogICAgICAgICAsIHVuaXR5X2NvZWZzPUYpCiAgLCBsaXN0KG5hbWU9InhfZzExMF9uc19sYWRkZXIiLCB0eXBlPSJuc19sYWRkZXIiLCBtYXR1cml0eT0xMTAKICAgICAgICAgLCBmZWF0dXJlcz1jKCJ0b3RfcmV0X25zXzEiLCAidG90X3JldF9uc18yIiwgInRvdF9yZXRfbnNfMyIsICJ0b3RfcmV0X25zXzUiLCAidG90X3JldF9uc183IiwgInRvdF9yZXRfbnNfMTAiKQogICAgICAgICAsIHVuaXR5X2NvZWZzPUYpCikKCmc3X2ZlYXR1cmVzIDwtIHNvcnQodW5pcXVlKHJiaW5kbGlzdChsYXBwbHkoZzdfbW9kZWxzLCBmdW5jdGlvbih4KSBkYXRhLnRhYmxlKGZlYXR1cmU9eCRmZWF0dXJlcykpKSkkZmVhdHVyZSkKCmBgYAoKIyMjIE1vZGVsIGV2YWx1YXRpb24gLSBmdWxsIGhpc3RvcnkKYGBge3J9CmV2YWx1YXRlIDwtIGZ1bmN0aW9uKG1vZGVsLCBwZXJfaW5feWVhcj0xMiwgd3RzPU5VTEwpIHsKICBpZiAoaXMubnVsbCh3dHMpKQogICAgd3RzIDwtIHJlcCgxL2xlbmd0aChtb2RlbCR5KSwgbGVuZ3RoKG1vZGVsJHkpKQogIGVsc2UKICAgIHd0cyA8LSB3dHMgLyBzdW0od3RzKQogIAogIHlfaGF0IDwtIG1vZGVsJFggJSolIG1vZGVsJGNvZWZzCiAgcmVzaWR1YWxzIDwtIHlfaGF0IC0gbW9kZWwkeQogIHJldHVybigKICAgIGMoCiAgICAgIFIyID0gY2FpbTo6d3RkX2Nvcih5X2hhdCwgbW9kZWwkeSwgd3RzPXd0cykgXiAyCiAgICAgICMgLCBNU0UgPSBjYWltOjp3dGRfbWVhbihyZXNpZHVhbHNeMiwgd3RzPXd0cykgIyBjYWltOjp3dGRfbWVhbihyZXNpZHVhbHNeMiwgd3RzPXd0cykKICAgICAgLCBSTVNFID0gc3FydChjYWltOjp3dGRfbWVhbihyZXNpZHVhbHNeMiwgd3RzPXd0cykpCiAgICAgICMgLCBhbHBoYSA9IG1lYW4ocmVzaWR1YWxzKSAjIGNhaW06Ond0ZF9tZWFuKHJlc2lkdWFscywgd3RzPXd0cykKICAgICAgIyAsIHRlID0gc2QocmVzaWR1YWxzKSAjIGNhaW06Ond0ZF9zZChyZXNpZHVhbHMsIHd0cz13dHMpCiAgICAgICwgYWxwaGFfeSA9ICgxICsgY2FpbTo6d3RkX21lYW4ocmVzaWR1YWxzLCB3dHM9d3RzKSkgXiBwZXJfaW5feWVhciAtIDEKICAgICAgLCB0ZV95ID0gY2FpbTo6d3RkX3NkKHJlc2lkdWFscywgd3RzPXd0cykgKiBzcXJ0KHBlcl9pbl95ZWFyKQogICAgKQogICkKfQoKIyBmb3IgZXZlcnkgY29tYmluYXRpb24gb2YgY291bnRyeSBhbmQgbWF0dXJpdHkKaGlzdF9ldmFsX2xpc3QgPC0gYXBwbHkoZzdfYXNzZXRzLCAxLCBmdW5jdGlvbihhc3NldCkgewogIHlfZGF0YSA8LSBnN19hc3NldF9kYXRhW2lkPT1hc3NldFsiaWQiXV0KICB4X2RhdGEgPC0gZzdfcmV0dXJuX2RhdGFbY291bnRyeT09YXNzZXRbImNvdW50cnkiXV1bCiAgICAsIGMoInllYXIiLCAibW9udGgiKSA6PSAuKAogICAgICBsdWJyaWRhdGU6OnllYXIoZGF0ZSkKICAgICAgLCBsdWJyaWRhdGU6Om1vbnRoKGRhdGUpCiAgICApCiAgXQogICMgcHJpbnQocGFzdGUoYXNzZXRbImNvdW50cnkiXSwgYXNzZXRbIm1hdHVyaXR5Il0sIG5yb3coeV9kYXRhKSwgbnJvdyh4X2RhdGEpKSkKICBtb2RlbF9kYXRhIDwtIHlfZGF0YVsKICAgIHhfZGF0YQogICAgLAogICAgLCBvbj0uKHllYXIsIG1vbnRoKSwgbm9tYXRjaD1OVUxMCiAgXVsKICAgICwgIWMoCiAgICAgICJjb3VudHJ5IiwgImkuY291bnRyeSIsICJjbGFzcyIsICJtYXR1cml0eSIsICJ5ZWFyIiwgIm1vbnRoIiwgImluZGV4X25hdiIsICJpLmRhdGUiCiAgICApCiAgXQoKICBoYXNfaW52YWxpZCA8LSByb3dTdW1zKGlzLm5hKG1vZGVsX2RhdGFbICwgLi5nN19mZWF0dXJlc10pKSA+IDAKICBwcmludChwYXN0ZShhc3NldFsiaWQiXSwgInJvd3M6IiwgbnJvdyhtb2RlbF9kYXRhKSwgImludmFsaWQ6Iiwgc3VtKGhhc19pbnZhbGlkKSkpCgogIG1vZGVsX2RhdGEgPC0gbW9kZWxfZGF0YVshaGFzX2ludmFsaWRdCiAgcHJpbnQocGFzdGUoImNsZWFuIHJvd3M6IiwgbnJvdyhtb2RlbF9kYXRhKSkpCiAgCiAgYXNzZXRfbW9kZWxzIDwtIGxhcHBseSh3aGljaChzYXBwbHkoZzdfbW9kZWxzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBmdW5jdGlvbih4KSB4JG1hdHVyaXR5PT1hcy5udW1lcmljKGFzc2V0WyJtYXR1cml0eSJdKSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAsIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgZzdfbW9kZWxzW1t4XV0gCiAgICAgICAgICAgICAgICAgICAgICAgICB9KQogIAogIHJlcyA8LSBsYXBwbHkoYXNzZXRfbW9kZWxzLCBmdW5jdGlvbiAobSkgewogICAgcHJpbnQocGFzdGUoYXNzZXRbImlkIl0sIG0kbmFtZSkpCiAgICB5IDwtIG1vZGVsX2RhdGFbICwgaW5kZXhfcmV0XQogICAgWCA8LSBjYmluZChpbnRlcmNlcHQ9MSwgbW9kZWxfZGF0YVsgLCBtJGZlYXR1cmVzLCB3aXRoPUZdKQogICAgCiAgICBBZXEgPC0gYygwLCByZXAoMSwgbmNvbChYKSAtIDEpKQogICAgYmVxIDwtIDEKICAgIGxiIDwtIGMoY2FpbTo6TkVHX0lORiwgcmVwKDAsIG5jb2woWCkgLSAxKSkKICAgIHd0cyA8LSBjYWltOjpkX2V4cChjYWltOjpoYWxmbGlmZTJkZWNheSgxMiksIG5yb3coWCkpCgogICAgZml4ZWRfY29lZnMgPC0gTlVMTAogICAgaWYgKG0kdW5pdHlfY29lZnMpCiAgICAgIGZpeGVkX2NvZWZzIDwtIGMoMCwgcmVwKDEvbGVuZ3RoKG0kZmVhdHVyZXMpLCBsZW5ndGgobSRmZWF0dXJlcykpKQoKICAgIGxtcSA8LSBjYWltOjpsbV9xdWFkKHksIFgsIEFlcT1BZXEsIGJlcT1iZXEsIGxiPWxiLCB3dHM9d3RzLCBmaXhlZF9jb2Vmcz1maXhlZF9jb2VmcykKICAgICMgIyBjb2VmcyA8LSByb3VuZChsbXEkY29lZnMsIDYpCiAgICAjICMgcHJpbnQocGFzdGUoeCwgY29lZnMpKQoKICAgICMgcHJpbnQobG1xJGNvZWZzKQogICAgIyByZXR1cm4ocm91bmQoZXZhbHVhdGUobG1xKSwgNikpCiAgICAKICAgIGNvZWZzIDwtIHJvdW5kKGxtcSRjb2VmcywgNikKCiAgICByZXR1cm4oZGF0YS50YWJsZShhc3NldD1hc3NldFsiaWQiXQogICAgICAgICAgICwgbW9kZWw9bSRuYW1lCiAgICAgICAgICAgLCB0KHJvdW5kKGV2YWx1YXRlKGxtcSwgd3RzPXd0cyksIDYpKQogICAgICAgICAgICwgY29lZnM9bGlzdChjb2VmcykKICAgICAgICAgICAsIGZlYXR1cmVzPWxpc3QobmFtZXMoY29lZnMpKQogICAgICAgICAgKSkKICAgICMgcmV0dXJuKGxpc3QobmFtZT1tJG5hbWUsIGNvZWZzPXJvdW5kKGxtcSRjb2VmcywgNiksIGV2YWw9cm91bmQoZXZhbHVhdGUobG1xKSwgNikpKQogICAgIyByZXR1cm4obnJvdyhYKSkKICB9KQoKICAjIHkgPC0gbW9kZWxfZGF0YVsgLCBpbmRleF9yZXRdCiAgIyBYIDwtIGNiaW5kKGludGVyY2VwdD0xLCBtb2RlbF9kYXRhWyAsIG1vZGVsJGZlYXR1cmVzXSkKCiAgcmV0dXJuKHJlcykKCn0pCgojIGNvbWJpbmUgZXZhbF9saXN0LCB3aGljaCBpcyBhIFtbbnVtYXNzZXRzXV1bW251bW1vZGVsc11dIGxpc3QKaGlzdF9ldmFsIDwtIHJiaW5kbGlzdChsYXBwbHkoaGlzdF9ldmFsX2xpc3QsIHJiaW5kbGlzdCkpCmhpc3RfZXZhbFtdCmBgYAoKIyMjIEstZm9sZCBjcm9zcyB2YWxpZGF0aW9uCmBgYHtyfQprX2ZvbGRfZXZhbCA8LSByYmluZGxpc3QoYXBwbHkoZzdfYXNzZXRzLCAxLCBmdW5jdGlvbihhc3NldCkgewogIHlfZGF0YSA8LSBnN19hc3NldF9kYXRhW2lkPT1hc3NldFsiaWQiXV0KICB4X2RhdGEgPC0gZzdfcmV0dXJuX2RhdGFbY291bnRyeT09YXNzZXRbImNvdW50cnkiXV1bCiAgICAsIGMoInllYXIiLCAibW9udGgiKSA6PSAuKAogICAgICBsdWJyaWRhdGU6OnllYXIoZGF0ZSkKICAgICAgLCBsdWJyaWRhdGU6Om1vbnRoKGRhdGUpCiAgICApCiAgXQogICMgcHJpbnQocGFzdGUoYXNzZXRbImNvdW50cnkiXSwgYXNzZXRbIm1hdHVyaXR5Il0sIG5yb3coeV9kYXRhKSwgbnJvdyh4X2RhdGEpKSkKICBtb2RlbF9kYXRhIDwtIHlfZGF0YVsKICAgIHhfZGF0YQogICAgLAogICAgLCBvbj0uKHllYXIsIG1vbnRoKSwgbm9tYXRjaD1OVUxMCiAgXVsKICAgICwgIWMoCiAgICAgICJjb3VudHJ5IiwgImkuY291bnRyeSIsICJjbGFzcyIsICJtYXR1cml0eSIsICJ5ZWFyIiwgIm1vbnRoIiwgImluZGV4X25hdiIsICJpLmRhdGUiCiAgICApCiAgXQoKICBoYXNfaW52YWxpZCA8LSByb3dTdW1zKGlzLm5hKG1vZGVsX2RhdGFbICwgLi5nN19mZWF0dXJlc10pKSA+IDAKICAjIHByaW50KHBhc3RlKGFzc2V0WyJpZCJdLCAicm93czoiLCBucm93KG1vZGVsX2RhdGEpLCAiaW52YWxpZDoiLCBzdW0oaGFzX2ludmFsaWQpKSkKCiAgbW9kZWxfZGF0YSA8LSBtb2RlbF9kYXRhWyFoYXNfaW52YWxpZF0KICAjIHByaW50KHBhc3RlKCJjbGVhbiByb3dzOiIsIG5yb3cobW9kZWxfZGF0YSkpKQogIAogIGtfaXggPC0gY2FpbTo6a19mb2xkX2l4KG5yb3cobW9kZWxfZGF0YSksIE5VTV9LX0ZPTERTKQogIAogIGFzc2V0X21vZGVscyA8LSBsYXBwbHkod2hpY2goc2FwcGx5KGc3X21vZGVscwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgZnVuY3Rpb24oeCkgeCRtYXR1cml0eT09YXMubnVtZXJpYyhhc3NldFsibWF0dXJpdHkiXSkpKQogICAgICAgICAgICAgICAgICAgICAgICAgLCBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgIGc3X21vZGVsc1tbeF1dIAogICAgICAgICAgICAgICAgICAgICAgICAgfSkKICAKICBtb2RlbF9yZXMgPC0gbGFwcGx5KGFzc2V0X21vZGVscywgZnVuY3Rpb24gKG0pIHsKICAgIHByaW50KHBhc3RlKGFzc2V0WyJpZCJdLCBtJG5hbWUpKQogICAgeSA8LSBtb2RlbF9kYXRhWyAsIGluZGV4X3JldF0KICAgIFggPC0gY2JpbmQoaW50ZXJjZXB0PTEsIG1vZGVsX2RhdGFbICwgbSRmZWF0dXJlcywgd2l0aD1GXSkKICAgIAogICAga19yZXMgPC0gc2FwcGx5KHNlcSgxLCBOVU1fS19GT0xEUyksIGZ1bmN0aW9uKGZvbGQpIHsKICAgICAgdHJhaW5faXggPC0ga19peFtrICE9IGZvbGQsIGl4XQogICAgICB0ZXN0X2l4IDwtIGtfaXhbayA9PSBmb2xkLCBpeF0KICAgICAgCiAgICAgIHRyYWluX3kgPC0geVt0cmFpbl9peF0KICAgICAgdHJhaW5fWCA8LSBYW3RyYWluX2l4XQoKICAgICAgQWVxIDwtIGMoMCwgcmVwKDEsIG5jb2wodHJhaW5fWCkgLSAxKSkKICAgICAgYmVxIDwtIDEKICAgICAgbGIgPC0gYyhjYWltOjpORUdfSU5GLCByZXAoMCwgbmNvbCh0cmFpbl9YKSAtIDEpKQogICAgICB3dHMgPC0gY2FpbTo6ZF9leHAoY2FpbTo6aGFsZmxpZmUyZGVjYXkoMTIpLCBucm93KHRyYWluX1gpKQogIAogICAgICBmaXhlZF9jb2VmcyA8LSBOVUxMCiAgICAgIGlmIChtJHVuaXR5X2NvZWZzKQogICAgICAgIGZpeGVkX2NvZWZzIDwtIGMoMCwgcmVwKDEvbGVuZ3RoKG0kZmVhdHVyZXMpLCBsZW5ndGgobSRmZWF0dXJlcykpKQogICAgICAKICAgICAgbG1xIDwtIGNhaW06OmxtX3F1YWQodHJhaW5feSwgdHJhaW5fWAogICAgICAgICAgICAgICAgICAgICAgICAgICAsIEFlcT1BZXEKICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBiZXE9YmVxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICwgbGI9bGIKICAgICAgICAgICAgICAgICAgICAgICAgICAgLCB3dHM9d3RzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICwgZml4ZWRfY29lZnM9Zml4ZWRfY29lZnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAKICAgICAgdGVzdF95IDwtIHlbdGVzdF9peF0KICAgICAgdGVzdF9YIDwtIFhbdGVzdF9peF0KICAgICAgCiAgICAgICMgcHJpbnQodGVzdF9YKQogICAgICB0ZXN0X3lfaGF0IDwtIGFzLm1hdHJpeCh0ZXN0X1gpICUqJSBsbXEkY29lZnMKICAgICAgcmVzaWR1YWxzIDwtIHRlc3RfeV9oYXQgLSB0ZXN0X3kKICAgICAgUjIgPC0gY29yKHRlc3RfeSwgdGVzdF95X2hhdCkgXiAyCiAgICAgIFJNU0UgPC0gc3FydChtZWFuKHJlc2lkdWFsc14yKSkKICAgICAgYWxwaGFfeSA8LSAoMSArIG1lYW4ocmVzaWR1YWxzKSkgXiAxMiAtIDEKICAgICAgdGVfeSA8LSBzZChyZXNpZHVhbHMpICogc3FydCgxMikKCiAgICAgIHJldHVybihyb3VuZChjKGs9Zm9sZCwgUjI9UjIsIFJNU0U9Uk1TRSwgYWxwaGFfeT1hbHBoYV95LCB0ZV95PXRlX3kpLCA2KSkKCiAgICB9KQogICAgCiAgICByZXR1cm4oZGF0YS50YWJsZShhc3NldD1hc3NldFsiaWQiXSwgbW9kZWw9bSRuYW1lLCB0eXBlPW0kdHlwZSwgdChrX3JlcykpKQoKICB9KQoKICByZXR1cm4ocmJpbmRsaXN0KG1vZGVsX3JlcykpCgp9KSlbCiAgLCAuKAogICAgUjJfbWVhbiA9IG1lYW4oUjIpCiAgICAsIFJNU0VfbWVhbiA9IG1lYW4oUk1TRSkKICAgICwgYWxwaGFfeV9tZWFuID0gbWVhbihhbHBoYV95KQogICAgLCB0ZV95X21lYW4gPSBtZWFuKHRlX3kpCiAgICAsIFIyX3NkID0gc2QoUjIpCiAgICAsIFJNU0Vfc2QgPSBzZChSTVNFKQogICAgLCBhbHBoYV95X3NkID0gc2QoYWxwaGFfeSkKICAgICwgdGVfeV9zZCA9IHNkKHRlX3kpCiAgICAsIHNjb3JlID0gbWVhbihSTVNFKSAqIHNkKFJNU0UpCiAgKQogICwgYnk9Lihhc3NldCwgbW9kZWwsIHR5cGUpCiAgXVsKICAgIGc3X2Fzc2V0cywgb249Yyhhc3NldD0iaWQiKQogIF1bCiAgICAsIC5TRAogICAgLCBrZXk9Lihjb3VudHJ5LCBtYXR1cml0eSwgc2NvcmUpCiAgXQoKa19mb2xkX2Jlc3RfbW9kZWxzIDwtIGtfZm9sZF9ldmFsWwogICwgaGVhZCguU0QsIDMpCiAgLCBieT0uKGNvdW50cnksIG1hdHVyaXR5LCBhc3NldCkKICBdCgpiZXN0X21vZGVsX2ZyZXEgPC0gc29ydCh0YWJsZShrX2ZvbGRfYmVzdF9tb2RlbHMkdHlwZSksIGRlY3JlYXNpbmc9VCkKYmVzdF9tb2RlbF9mcmVxCmBgYAoKIyMjIE9sZCBhcHBlbmRpeDogY29uc3RyYWluZWQgbGluZWFyIGxlYXN0IHNxdWFyZXMgPSBxdWFkcmF0aWMgcHJvZ3JhbW1pbmcKYGBge3J9CnQoc2FwcGx5KG5hbWVzKGcxM19tb2RlbHMpLCBmdW5jdGlvbih4KSB7CiAgbW9kZWwgPC0gZzEzX21vZGVsc1tbeF1dCiAgeSA8LSBtb2RlbF9kYXRhWyAsIGluZGV4X3JldF0KICBYIDwtIGNiaW5kKGludGVyY2VwdD0xLCBtb2RlbF9kYXRhWyAsIG1vZGVsJGZlYXR1cmVzLCB3aXRoPUZdKQoKICBBZXEgPC0gYygwLCByZXAoMSwgbmNvbChYKSAtIDEpKQogIGJlcSA8LSAxCiAgbGIgPC0gYyhjYWltOjpORUdfSU5GLCByZXAoMCwgbmNvbChYKSAtIDEpKQogIHd0cyA8LSBjYWltOjpkX2V4cChjYWltOjpoYWxmbGlmZTJkZWNheSgxMiksIG5yb3coWCkpCiAgCiAgZml4ZWRfY29lZnMgPC0gTlVMTAogIGlmIChtb2RlbCR1bml0eV9jb2VmcykKICAgIGZpeGVkX2NvZWZzIDwtIGMoMCwgcmVwKDEvbGVuZ3RoKG1vZGVsJGZlYXR1cmVzKSwgbGVuZ3RoKG1vZGVsJGZlYXR1cmVzKSkpCgogIGxtcSA8LSBjYWltOjpsbV9xdWFkKHksIFgsIEFlcT1BZXEsIGJlcT1iZXEsIGxiPWxiLCB3dHM9d3RzLCBmaXhlZF9jb2Vmcz1maXhlZF9jb2VmcykKICAjIGNvZWZzIDwtIHJvdW5kKGxtcSRjb2VmcywgNikKICAjIHByaW50KHBhc3RlKHgsIGNvZWZzKSkKICAKICBwcmludChsbXEkY29lZnMpCiAgcmV0dXJuKHJvdW5kKGV2YWx1YXRlKGxtcSksIDYpKQogICMgcmV0dXJuKGFzLm1hdHJpeChjKG1vZGVsPXgsIHQocm91bmQoZXZhbHVhdGUobG1xKSwgNikpKSkpCn0pKQoKYGBgCgo=